Can't write to ACF custom field in a WooCommerce order - php

I can't write a value to an ACF custom field on a WooCommerce order. I can read the same field. How can I update this ACF field on the order?
// autocomplete virtual order + set backorder flag
add_action('woocommerce_order_status_changed', 'ke_set_backorder_flag');
function ke_set_backorder_flag($order_id)
{
if ( ! $order_id ) {
return;
}
global $product;
//get an instance of the WC_order object
$order = wc_get_order( $order_id );
// loop to get the order items from WC_Order_Item_Product objects
foreach( $order->get_items() as $item_id => $item ) {
//only look at line items (products)
if ( $item['type'] == 'line_item' ) {
$_product = $item->get_product();
$ke_stock_qty = $_product->get_stock_quantity();
//check if backorder
if ( $ke_stock_qty < 1 ) {
// TEST dst - read ke_stock ACF custom field
$field_key='field_604a3d0d86088';
$ke_stock_value = get_field( $field_key );
// TEST write the actual stock qty in ke_stock
update_field( $field_key, $ke_stock_qty );
}
}
}
}
My goal eventually is to set a boolean field as a backorder flag which I can show in the order view. But for now I'm just trying to update a text field.
Any help is much appreciated.

Related

Add products to order after payment (WooCommerce/Wordpress)

I am creating a 'Build Your Own' page. After selecting 4 products from the options, 1 main product gets added to the basket with 4 of the selected products being added as meta_data under that main product.
You can see below the main product with 4 selected products (IDs).
After paying for this item, I need to add each selected product to the order, so that they are within the order on the backend. I'm having to do it like this, because I need the stock of the selected product to go down, eventually pulling into a stock management system we use (veeqo)
Any help is appreciated. The code below allows me get the some meta_data for woocommerce_thankyou but I am not sure if it will work then...
add_action('woocommerce_thankyou', 'BuildYourOwn', 10, 1);
function BuildYourOwn( $order_id ) {
if ( !$order_id ){
return;
}
$firstTime = get_post_meta( $order_id, '_thankyou_action_done', true );
// Allow code execution only once
if( !$firstTime ) {
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
$exItems = '';
// Loop through order items
foreach ( $order->get_items() as $item_id => $item ) {
//print_r($item);
// Get the product object
$product = $item->get_product();
// Get the product sku
$product_sku = $product->get_sku();
// Get the product name
$product_id = $product->get_name();
$extras = $item->get_formatted_meta_data('_', true);
$exItems.=$product_sku;
if(!empty($extras)){
$exItems.=$product_sku.' -';
foreach($extras as $extra){
$exItems.= ' ['.$extra->key.' : '. preg_replace("/[^A-Za-z0-9?#,.&%!\s]/","",$extra->value).'] ';
}
}
$exItems.="\n";
}
var_dump($exItems);
Maybe I worded the question wrong - But I figured it out:
I used the code below to get the meta_data which I then looped through and got individual item id and added it to the basket this way.
// Get the product meta data
$extras = $item->get_formatted_meta_data('_', true);
if ($product_id == 60023){
if(!empty($extras)){
$args = array(
'subtotal' => 0,
'total' => 0,
);
foreach($extras as $extra){
$mini_name = $extra->value;
$mini = get_page_by_title( $mini_name, OBJECT, 'product' );
$mini_id = $mini->ID;
$order->add_product( wc_get_product($mini_id), 1, $args); // Add Minis
}
}
}
The only slight issue is woocommerce_thankyou hook not firing if paypal users don't come back to the site after paying.

Save product custom meta as order item meta when manually adding products to WooCommerce admin orders

I'm trying to add custom product meta to order item meta when adding products to the order from the admin. This is my code, which does nothing in the backend ...
// Order items: Save product "location" as order item meta data
add_action( 'woocommerce_ajax_add_order_item_meta', 'action_before_save_order_item_callback' );
function action_before_save_order_item_callback($item, $cart_item_key, $values, $order) {
if ( $location = $values['data']->get_meta('location') ) {
$item->update_meta_data( 'location', $location ); // Save as order item meta
}
}
You are not using the right function hook arguments which are $item_id, $item, $order and not using the right way. Try the following instead (code is commented):
add_action( 'woocommerce_ajax_add_order_item_meta', 'action_before_save_order_item_callback', 9999, 3 );
function action_before_save_order_item_callback( $item_id, $item, $order ) {
$product = $item->get_product(); // Get the WC_Product Object
$location = $product->get_meta('location'); // Get custom meta data
// If custom field is empty on a product variation check on the parent variable product
if( empty($location) && $item->get_variation_id() > 0 ) {
$parent_product = wc_get_product( $item->get_product_id() ); // Get parent WC_Product Object
$location = $product->get_meta('location'); // Get custom meta data
}
// If product meta data exist
if( ! empty($location) ) {
$item->update_meta_data( 'location', $location ); // Set it as order item meta
$item->save(); // save it
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.

Save custom delivery date from product attribute on WooCommerce order

i have a custom attribute where products have different pickup times. i want to add this dates as a custom field to my wc orders, to notify my customers via cronjob that the order is ready to pickup.
with the code below i get the wrong date, can anyone tell me what is wrong here?
foreach ( $order->get_items() as $item_id => $item ) {
$product_id = $item->get_product_id();
$order_id = $order->get_id();
$new_product = new WC_Product( $product_id ); // create an object of WC_Product class
//$product_attribut = $new_product->get_attribute( 'pa_lieferfrequenz' ); // call get_attribute method
$product_attribut = '1week';
$date = date('d-m-Y', strtotime("+ ' . $product_attribut . '"));
add_post_meta( $order_id, 'lwb_pickup_time_email_notification', $date );
}
It's better to loop through cart items and save the date in this custom hooked function, once order is placed before saving data:
add_action( 'woocommerce_checkout_create_order', 'wc_checkout_create_order_action_callback' );
function wc_checkout_create_order_action_callback( $order ) {
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
$lieferfrequenz = $cart_item['data']->get_attribute( 'pa_lieferfrequenz' );
if ( ! empty( $lieferfrequenz ) ) {
// Save the date as custom order meta data
$order->update_meta_data( 'lwb_pickup_time_email_notification', date('d-m-Y', strtotime("+ '.$lieferfrequenz.'") ) );
break; /// stop the loop
}
}
}
Code goes in functions.php file of your active child theme (or active theme). It should works.
But if there is many different cart items on an order, you should re-think things differently, as this code will take the first cart item delivery date (or the last one, if you remove break;).
Related: Get Order items and WC_Order_Item_Product in WooCommerce 3

Custom post type data and order item data in WooCommerce

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.

Product attribute empty value from order item in Woocommerce 3

I know that there are a lot of questions already for that matter but im not able to figure out how to get a custom product attribute from an woocommerce order. here is what i have try:
$order = wc_get_order( $order_id );
$order_data = $order->get_data();
foreach ($order->get_items() as $item_key => $item_values) {
$product = $item_values->get_product(); // Get the product
$product_id = $item_values->get_product_id(); // the Product id
$tokens = get_post_meta($product_id, 'Tokens', true);
}
I have also try:
$tokens = $product->get_attribute( 'Tokens' );
and
$tokens = array_shift( wc_get_product_terms( $product_id, 'Tokens', array( 'fields' => 'names' ) ) );
My custom product attribute has the name "Tokens" and the value 5000 but im getting an empty return,
what am i doing wrong?
That can happen for a variable product when the product attribute is not set as an attribute for variations.
So when you have a product variation as order item, you need to get the parent variable product to get your product attribute value (if this product attribute is not set as an attribute for variations).
If it is the case for "Tokens" product attribute, try the following:
$attribute = 'Tokens';
$order = wc_get_order( 857 );
// Loop through order line items
foreach ( $order->get_items() as $item_id => $item ) {
$product = $item->get_product(); // Get the WC_Product object
// For Product Variation type
if( $item->get_variation_id() > 0 ){
$parent = wc_get_product($product->get_parent_id());
$term_names = $parent->get_attribute($attribute);
}
// For other Product types
else {
$term_names = $product->get_attribute($attribute);
}
// Testing display (the string of coma separated term names if many)
if( ! empty( $term_name ) )
echo '<p>'.$term_name.'</p>';
}
Tested and works in Woocommerce 3+

Categories