In WooCommerce, I understand well that woocommerce_get_order_item_totals filter kook is used to customize order total rows like reordering them.
add_filter( 'woocommerce_get_order_item_totals', 'custom_order_of_from_order_table', 10, 2 );
function woocommerce_get_order_item_totals( $total_rows, $order ) {
// code here
return $total_rows;
}
I have tried to reorder the subtotal over the total, and the payment method below the total without success on WooCommerce ThankYou page. My PHP knowledge is very limited and I appreciate any help.
How to customize total rows from order table, reordering them on WooCommerce thankyou page?
The following will reorder items totals as desired on Woocommerce thankyou (order received) page only:
add_filter( 'woocommerce_get_order_item_totals', 'reordering_order_item_totals', 10, 3 );
function reordering_order_item_totals( $total_rows, $order, $tax_display = '' ){
// Only on "order received" thankyou page
if ( ! is_wc_endpoint_url('order-received') )
return $total_rows;
$sorted_items_end = array('cart_subtotal', 'order_total', 'payment_method');
$sorted_total_rows = array(); // Initializing
// Loop through sorted totals item keys
foreach( $sorted_items_end as $item_key ) {
if( isset($total_rows[$item_key]) ) {
$sorted_total_rows[$item_key] = $total_rows[$item_key]; // Save sorted data in a new array
unset($total_rows[$item_key]); // Remove sorted data from default array
}
}
return array_merge( $total_rows, $sorted_total_rows); // merge arrays
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
To make that work everywhere for customer orders and email notifications, just remove:
// Only on "order received" thankyou page
if ( ! is_wc_endpoint_url('order-received') )
return $total_rows;
Related
I need send text data from woocommerc cart page to ready order - by cart items data.
1) Set text to metafield in first product when click button GO TO CHECKOUT at CART PAGE
2) Get this metafield in Order data when order complited.
1) ADD METAFIELD TO CART ITEM .
$cart = WC()->cart->cart_contents;
foreach( $cart as $cart_item_id=>$cart_item ) {
$cart_item['my_metafield'] = "text";
WC()->cart->cart_contents[$cart_item_id] = $cart_item;
}
WC()->cart->set_session();
2) GET METAFIELD FROM ORDER ITEMS
add_action( 'woocommerce_order_status_complited' , 'get_my_metafield' , 10 , 1 );
function get_my_metafield ( $order ){
How get ['my_metafield'] ? Product don`t have 'my_metafield'
}
This function add Cart items metafield to Order items metafiels
add_action( 'woocommerce_checkout_create_order_line_item', function( $item, $cart_item_key, $values, $order )
{
if ( isset( $values['my_metafield'] ) ) {
$item->add_meta_data( "my_metafield", $values['my_metafield'] , true );
}
}, 10, 4 );
I am using a plugin, WooCommerce Custom Post Type Manager, along with WooCommerce that enables the use of custom post types as products. This is working pretty well for the most part, but I want to be able to control inventory and I am able to see the problem. In the database under the order_item_meta table, the product_id = 0. Because the product_id is empty, there is no way to update the stock on a completed purchase.
I know that WooCommerce made some changes to where it searched if a post_type was a 'product' and if not, certain things failed. I am wondering if there is a filter hook or another way to add the product id with a custom function at checkout?
This is the function I am trying to create to control a custom type of inventory, we are selling event "seats". And no, even the regular "stock" does not work, probably because of the same reason.
function update_course_seats( $order_id ){
$order = wc_get_order( $order_id );
$items = $order->get_items();
foreach ( $items as $item ) {
$product_id = $item->get_product_id(); // this returns 0
if($product_id != 0) {
wc_update_order_item_meta( $item, '_seats', 10 ); // example number
}
}
//add_action( 'woocommerce_payment_complete', 'update_course_seats');
First you should use woocommerce_add_cart_item_data action hook, to store your Custom Post Type (CTP) Id on add to cart… But for that you will need to display, on your CTP pages, inside the add to cart form, a hidden field with the CTP Id like:
<input type="hidden" name="ctpost_id" value="<?php echo get_the_id(); ?>">
Now you can add this hooked function, that will add as custom cart item data your CTP Id:
add_filter( 'woocommerce_add_cart_item_data', 'add_custom_cart_item_data', 10, 3 );
function add_custom_cart_item_data( $cart_item_data, $product_id, $variation_id ){
if( isset( $_POST['ctpost_id'] ) ) {
$cart_item_data['ctpost_id'] = wc_clean( $_POST['ctpost_id'] );
}
return $cart_item_data;
}
The Second hook to be used is woocommerce_checkout_create_order_line_item action hook, to add custom order item meta data (or to make changes to order item data). This hook is triggered before payment gateways process during order creation.
You can use WC_data add_meta_data() method to save your CTP Id as a custom CTP ID like:
add_action( 'woocommerce_checkout_create_order_line_item', 'save_cpt_id_to_order_item_data', 10, 4 );
function save_cpt_id_to_order_item_data( $item, $cart_item_key, $cart_item, $order ){
if( isset($cart_item['ctpost_id']) && $cart_item['ctpost_id'] > 0 ) {
// Add the custom CTP post ID
$item->add_meta_data('_ctpost_id', $cart_item['ctpost_id'] );
}
// And here for example you add seats as custom cart item data
if( isset($cart_item['quantity']) ) {
$item->add_meta_data( 'Seats', $cart_item['quantity'], true );
}
}
When the order will get paid, you will be able to update everything required, as you will have your CTP post ID as custom order item data.
Then finally, you can use woocommerce_payment_complete as follow:
add_action( 'woocommerce_payment_complete', 'action_payment_complete_callback', 10, 1 );
function action_payment_complete_callback( $order_id ){
$order = wc_get_order();
// Loop through order items
foreach ( $order->get_items() as $item_id => $item ) {
$ctp_id = $item->get_meta('_ctpost_id'); // Get the CTP post ID
// Your code goes here
}
}
Or woocommerce_order_status_changed hooks like:
add_action( 'woocommerce_order_status_changed', 'action_order_status_changed_callback', 10, 4 );
function action_order_status_changed_callback( $order_id, $status_from, $status_to, $order ){
if( in_array( $status_to, ['processing','completed'] ) ) {
// Loop through order items
foreach ( $order->get_items() as $item_id => $item ) {
$ctp_id = $item->get_meta('_ctpost_id'); // Get the CTP post ID
// Your code goes here
}
}
}
Related: Get Order items and WC_Order_Item_Product in Woocommerce 3.
I have a function on my Woocommerce website that enables customers to set a custom amount to pay for a specific product, based on a value I'm passing through the URL.
I'm using the woocommerce_before_calculate_totals hook, and up until I upgraded to WC 3.3.5, it was working fine. Now, when I run the code, the checkout initially shows the custom amount.
However, once the loader has finished updating, it resets the price to '0' (i.e. displaying £0.00 the checkout page's total fields).
Here's that code:
add_action( 'woocommerce_before_calculate_totals', 'pay_custom_amount', 99);
function pay_custom_amount() {
$payment_value = $_GET['amount'];
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if($cart_item['data']->id == 21 ){
$cart_item['data']->set_price($payment_value);
}
}
}
Well, colour me baffled. I've trawled Stack Overflow for solutions but can't see any similar problems. I see the hook runs multiple times but gather this is normal.
If anyone know what might be happening here, it would be great if you could share.
You can't get a price from an URL and set it in woocommerce_before_calculate_totals action hook. This needs to be done differently.
In the below code:
the first hooked function will get that "amount" from the URl and will set it (register it) in cart item object as custom data.
the 2nd hooked function will read that amount from custom cart item data and will set it as the new price.
Now your the target product ID in your code need to be the same ID that the added to cart product.
The code:
// Get the custom "amount" from URL and save it as custom data to the cart item
add_filter( 'woocommerce_add_cart_item_data', 'add_pack_data_to_cart_item_data', 20, 2 );
function add_pack_data_to_cart_item_data( $cart_item_data, $product_id ){
if( ! isset($_GET['amount']) )
return $cart_item_data;
$amount = esc_attr( $_GET['amount'] );
if( empty($amount) )
return $cart_item_data;
// Set the custom amount in cart object
$cart_item_data['custom_price'] = (float) $amount;
$cart_item_data['unique_key'] = md5( microtime() . rand() ); // Make each item unique
return $cart_item_data;
}
// Alter conditionally cart item price based on product ID and custom registered "amount"
add_action( 'woocommerce_before_calculate_totals', 'change_conditionally_cart_item_price', 30, 1 );
function change_conditionally_cart_item_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// HERE set your targeted product ID
$targeted_product_id = 21;
foreach ( $cart->get_cart() as $cart_item ) {
// Checking for the targeted product ID and the registered "amount" cart item custom data to set the new price
if($cart_item['data']->get_id() == $targeted_product_id && isset($cart_item['custom_price']) )
$cart_item['data']->set_price($cart_item['custom_price']);
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
I amusing WooCommerce and with WooCommerce Bookings plugin. In their documentation there's this solution for booking multiple items by using the "persons" quantity.
So what the Bookings plugins does, it ads meta data to the variations, it also includes "persons" what we're currently using as an indicator for the amount of items being booked.
What we want to next is retrieve the meta data [Persons] from the product variations and display this as the item quantity.
What I've done so far is edit the woocommerce cart template and copied it to our child theme. (we can't use hooks since there's no hook available to use in the shop table).
</td>
<!-- Use [_persons] as product quantity -->
<td class="product-quantity--booking">
<?php
if ( ! empty( $cart_item['booking'] ) ) {
echo WC()->cart->get_item_data( $cart_item );
}
?>
</td>
What I need to figure out is, how do i filter these variations and only show the meta data I need? I would only need to show meta data "persons".
The next thing would be to filter only the start date and end date to display on the other meta data column.
[EDIT 18/02]
So I've found out that I can't use "get_item_date" since it does not take any arguments to show a specific value.
I tried to use "wc_display_item_meta", but I don't know how to select the item I need.
<!-- Use ['Persons'] as product quantity -->
<td class="product-quantity--booking">
<?php
if ( ! empty( $cart_item['booking'] ) ) {
$item_meta = wc_display_item_meta( $cart_item['booking']['Persons'])
echo $item_meta;
}
?>
</td>
By using the vardump I found the key I need is "['booking']['Persons']"
Maybe someone could help me to point out how to filter the array for needed items?
The wc_display_item_meta() function is not to be used in Cart or with cart items. It's explicitly for WC_Order_Item where $item argument is an Order Item.
You will see in the source code:
/**
* Display item meta data.
*
* #since 3.0.0
* #param WC_Order_Item $item Order Item.
* #param array $args Arguments.
* #return string|void
*/
function wc_display_item_meta( $item, $args = array() ) { /* … … */ }
So this function will not work in your case.
How to do it (2ways):
1) You can use two custom hooked functions, that will remove all booking data except 'Persons' and that will remove the quantity for your bookable products only.
// Display only 'Persons' for bookable products
add_filter( 'woocommerce_get_item_data', 'display_only_persons_for_bookings', 20, 2 );
function display_only_persons_for_bookings( $cart_data, $cart_item ){
// Check that is a bookable product and that is Cart page
if( ! isset($cart_item['booking']) || ! is_cart() ) return $cart_data; // Exit
// Loop through this cart item data
foreach($cart_data as $key =>$values){
// Checking that there is some data and that is Cart page
if( ! isset($values['name']) || ! is_cart() ) return $cart_data; // Exit
// Removing ALL displayed data except "Persons"
if( $values['name'] != __('Persons','woocommerce') ){
unset($cart_data[$key]);
}
}
return $cart_data;
}
// Remove cart quantity for bookable products
add_filter( 'woocommerce_cart_item_quantity', 'remove_cart_quantity_for_bookings', 20, 3 );
function remove_cart_quantity_for_bookings( $product_quantity, $cart_item_key, $cart_item ){
// Check that is a bookable product
if( isset($cart_item['booking']) )
$product_quantity = '';
return $product_quantity;
}
This code goes on function.php file of your active child theme (or theme).
Tested and works.
2) Or you can remove all booking displayed meta data and display the "Persons" in the quantity field:
// Remove displayed cart item meta data for bookable products
add_filter( 'woocommerce_get_item_data', 'remove_cart_data_for_bookings', 20, 2 );
function remove_cart_data_for_bookings( $cart_data, $cart_item ){
// Check that is a bookable product and that is Cart page
if( ! isset($cart_item['booking']) || ! is_cart() ) return $cart_data; // Exit
// Loop through this cart item data
foreach($cart_data as $key =>$values){
// Removing ALL displayed data
unset($cart_data[$key]);
}
return $cart_data;
}
// Add "Persons" to replace cart quantity for bookable products
add_filter( 'woocommerce_cart_item_quantity', 'replace_cart_quantity_for_bookings', 20, 3 );
function replace_cart_quantity_for_bookings( $product_quantity, $cart_item_key, $cart_item ){
// Check that is a bookable product
if( isset($cart_item['booking']) ){
$product_quantity = '<span style="text-align: center; display:inline-block;">'.$cart_item['booking']['Persons'].'<br>
<small>(' . __('persons','woocommerce') . ')</small><span>';
}
return $product_quantity;
}
This code goes on function.php file of your active child theme (or theme).
Tested and works
If you manage only booking products, you could rename "Quantity" column label to "Persons" in the template cart/cart.php (overriding it via your active theme).
With woocommerce, in my website I'd like to add in the cart page a select input where the user can select a value between two options, and depending on this value I will change the price.
so far, I could get the total and change it using this :
function action_woocommerce_before_cart_totals( ) {
global $woocommerce;
$woocommerce->cart->total = $woocommerce->cart->total*0.25;
var_dump( $woocommerce->cart->total);};
The issue is that when I go to checkout page it doesn't take the total calculated in functions.php
Thanks for helping me.
You can use woocommerce_review_order_before_order_total hook too at the same time, to display your custom price in checkout, this way:
add_action( 'woocommerce_review_order_before_order_total', 'custom_cart_total' );
add_action( 'woocommerce_before_cart_totals', 'custom_cart_total' );
function custom_cart_total() {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
WC()->cart->total *= 0.25;
//var_dump( WC()->cart->total);
}
The Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works.
Payment gateway always uses $order->get_total() variable to fetch cart grand total. So in order to tweak use this filter woocommerce_order_amount_total
for your function if you do follow below steps. Your payment gateway always shows the total you tweaked.
add_filter( 'woocommerce_order_amount_total', 'custom_cart_total' );
function custom_cart_total($order_total) {
return $order_total *= 0.25;
}