I'm trying to create an order programmatically. Using wc_create_order() this is pretty straightforward:
$myProduct = new WC_Product(100);
$order = wc_create_order();
$order->add_product($myProduct, 1);
$order->calculate_totals();
This works as expected, and an order is created for a simple product with ID 100 for the correct amount.
However, if I try to do this with a variation, it doesn't seem to behave correctly. After much trial and error, I got it to sort-of work this way:
$membershipProduct = new WC_Product_Variable(100);
$theMemberships = $membershipProduct->get_available_variations();
$trueProduct = new WC_Product(100);
$variationsArray = array();
foreach ($theMemberships as $membership) {
if ($membership['sku'] == $chosenVariation) {
$variationID = $membership['variation_id'];
$variationsArray = $membership['attributes'];
}
}
if ($variationID) {
$trueProduct->variation_id = $variationID;
}
$order = wc_create_order();
$order->add_product($trueProduct, 1, $variationsArray);
$order->calculate_totals();
However, although it does create the order with the correct product and the correct variation ID, the total for the order is always 0 (which, coincidentally, is the price of the first variation).
Originally I was trying $order->add_product() with the object created from new WC_Product_Variable(), but that resulted in no products being added to the order at all, which leads me to believe it's an issue with creating orders programmatically with variable products. However, following the WooCommerce source code for these calls, I can't see what I'm doing wrong.
Is there something I'm missing, or a better way to create an order with a variable product?
Solved it.
Even though I could have sworn I'd tried (and failed) doing it this way, the answer was to not add the parent product ($trueProduct in the example), but to add the variation product by its ID.
This may have failed previously because, as #helgatheviking noted, my $variationsArray was formatted incorrectly according to the source; I needed an array with a ['variation'] key to send the correct variation array of attributes.
In total, my working code now looks like this:
$membershipProduct = new WC_Product_Variable(100);
$theMemberships = $membershipProduct->get_available_variations();
$variationsArray = array();
foreach ($theMemberships as $membership) {
if ($membership['sku'] == $chosenVariation) {
$variationID = $membership['variation_id'];
$variationsArray['variation'] = $membership['attributes'];
}
}
if ($variationID) {
$varProduct = new WC_Product_Variation($variationID);
$order = wc_create_order();
$order->add_product($varProduct, 1, $variationsArray);
$order->calculate_totals();
}
if you already have the variation_id you can just do this
$product_variation = new WC_Product_Variation($variation_id);
$order = wc_create_order();
$args=array();
foreach($product_variation->get_variation_attributes() as $attribute=>$attribute_value){
$args['variation'][$attribute]=$attribute_value;
}
$order->add_product($product_variation, $product['quantity'], $args);
Here is the solution that worked for me:
function add_item_to_order( $order_id, $prod_id ) {
$order = wc_get_order( $order_id );
$_product = wc_get_product( $prod_id );
// Set values
$item = array();
$item['product_id'] = $_product->id;
$item['variation_id'] = isset( $_product->variation_id ) ? $_product->variation_id : '';
$item['variation_data'] = $item['variation_id'] ? $_product->get_variation_attributes() : '';
$item['name'] = $_product->get_title();
$item['tax_class'] = $_product->get_tax_class();
$item['qty'] = 1;
$item['line_subtotal'] = wc_format_decimal( $_product->get_price_excluding_tax() );
$item['line_subtotal_tax'] = '';
$item['line_total'] = wc_format_decimal( $_product->get_price_excluding_tax() );
$item['line_tax'] = '';
$item['type'] = 'line_item';
// Add line item
$item_id = wc_add_order_item( $order_id, array(
'order_item_name' => $item['name'],
'order_item_type' => 'line_item'
) );
// Add line item meta
if ( $item_id ) {
wc_add_order_item_meta( $item_id, '_qty', $item['qty'] );
wc_add_order_item_meta( $item_id, '_tax_class', $item['tax_class'] );
wc_add_order_item_meta( $item_id, '_product_id', $item['product_id'] );
wc_add_order_item_meta( $item_id, '_variation_id', $item['variation_id'] );
wc_add_order_item_meta( $item_id, '_line_subtotal', $item['line_subtotal'] );
wc_add_order_item_meta( $item_id, '_line_subtotal_tax', $item['line_subtotal_tax'] );
wc_add_order_item_meta( $item_id, '_line_total', $item['line_total'] );
wc_add_order_item_meta( $item_id, '_line_tax', $item['line_tax'] );
wc_add_order_item_meta( $item_id, '_line_tax_data', array(
'total' => array(),
'subtotal' => array() )
);
// Store variation data in meta
if ( $item['variation_data'] && is_array( $item['variation_data'] ) ) {
foreach ( $item['variation_data'] as $key => $value ) {
wc_add_order_item_meta( $item_id, str_replace( 'attribute_', '', $key ), $value );
}
}
}
$item['item_meta'] = $order->get_item_meta( $item_id );
$item['item_meta_array'] = $order->get_item_meta_array( $item_id );
$item = $order->expand_item_meta( $item );
$order->calculate_totals();
}
Related
I use WooCommerce with WordPress and I would like to update a custom field for the purchase of a single product at the time of payment validation with the display of the thank you page.
I started from this code but it does not work for the moment.
add_action( 'woocommerce_thankyou', 'checkout_update_user_meta' );
function checkout_update_user_meta( $order_id ) {
$order = new WC_Order( '$order_id' );
$user_id = $order->get_user_id();
$items = $order->get_items();
foreach ( $items as $item ) {
$product_id = $item['product_id'];
if ( $product_id == '777' ) {
update_user_meta( $user_id, 'masterclass-1', 'ok' );
}
}
}
add_action( 'woocommerce_thankyou', 'checkout_update_user_meta' );
function checkout_update_user_meta( $order_id ) {
$order = new WC_Order( $order_id );
$user_id = $order->get_user_id();
$items = $order->get_items();
foreach ( $items as $item ) {
$product_id = $item['product_id'];
if ( $product_id === 777 ) {
update_user_meta( $user_id, 'masterclass-1', 'ok' );
}
}
}
The issue with your code was that you wrapped the $order_id in single quotes. This line $order = new WC_Order( $order_id ); can also be replaced as $order = wc_get_order( $order_id );
I just modified your code to Update user meta field after woocommerce checkout according to your need. Please check my changes and hope it will work 👍
add_action( 'woocommerce_thankyou', 'checkout_update_user_meta' );
function checkout_update_user_meta( $order_id ) {
$order = wc_get_order( $order_id );
$user_id = $order->get_user_id();
$items = $order->get_items();
foreach ( $items as $item ) {
$product_id = $item->get_product_id();
if ( $product_id == 777 ) {
update_user_meta( $user_id, 'masterclass-1', 'ok' );
}
}
}
I made a code to save a value based on a meta key when a certain product is purchased (order status = completed).
I have written the code below to complete this task. However, the meta data does not appear to be saved? Any advice?
add_action( 'woocommerce_order_status_completed', 'wpglorify_change_role_on_purchase2' );
function wpglorify_change_role_on_purchase2( $order_id ) {
// get order object and items
$order = new WC_Order( $order_id );
$items = $order->get_items();
$user_is= $user->ID;
$meta_key = get_field("field");
$field = get_user_meta($user_is, $meta_key);
$meta_value = 5;
$product_id = 1289;
foreach ( $items as $item ) {
if( $product_id == $item['product_id'] && $order->user_id ) {
$user = new WP_User( $order->user_id );
update_user_meta($user_is, $field, $meta_value, '');
}
}
}
Your code contains some mistakes:
get_field() is a ACF function, and doesn't seem to apply here
You're using $user->ID; but $user is undefined
Furthermore, it is important to determine whether you want to update user meta or post meta data
1) To update user meta you can use:
// Update user meta
function action_woocommerce_order_status_completed( $order_id, $order ) {
// Product IDs
$product_id = 1289;
// Meta data
$meta_value = 5;
$meta_key = 'my_meta_key';
// Is a order
if ( is_a( $order, 'WC_Order' ) ) {
// Getting the user ID
$user_id = $order->get_user_id();
// User ID exists
if ( $user_id >= 1 ) {
// Loop through order items
foreach ( $order->get_items() as $key => $item ) {
// Compare
if ( $item->get_product_id() == $product_id ) {
// Update user meta
update_user_meta( $user_id, $meta_key, $meta_value );
// Break loop
break;
}
}
}
}
}
add_action( 'woocommerce_order_status_completed', 'action_woocommerce_order_status_completed', 10, 2 );
2) To update post meta you can use:
// Update post meta
function action_woocommerce_order_status_completed( $order_id, $order ) {
// Product IDs
$product_id = 1289;
// Meta data
$meta_value = 5;
$meta_key = 'my_meta_key';
// Is a order
if ( is_a( $order, 'WC_Order' ) ) {
// Loop through order items
foreach ( $order->get_items() as $key => $item ) {
// Compare
if ( $item->get_product_id() == $product_id ) {
// Add the meta data
$order->update_meta_data( $meta_key, $meta_value );
// Save
$order->save();
/** OR the older way **/
// update_post_meta( $order_id, $meta_key, $meta_value );
// Break loop
break;
}
}
}
}
add_action( 'woocommerce_order_status_completed', 'action_woocommerce_order_status_completed', 10, 2 );
add_filter( 'manage_edit-shop_order_columns', 'custom_shop_order_column',11);
function custom_shop_order_column($columns)
{
$reordered_columns = array();
// Woocommerce version 3.3+ compatibility
$location_after = version_compare( WC_VERSION, '3.3', '<' ) ? 'order_notes' : 'order_status';
// Inserting the new column in a specific location
foreach( $columns as $key => $column){
$reordered_columns[$key] = $column;
if( $key == $location_after ){
$reordered_columns['pvdl']= __('PVF/DL', 'woocommerce');
}
}
return $reordered_columns;
}
add_action( 'manage_shop_order_posts_custom_column' , 'custom_orders_list_column_content', 10, 2 );
function custom_orders_list_column_content( $column, $post_id )
{
if('pvdl' == $column){
//echo 'Working on it';
$order = wc_get_order($post_id);
$email = $order->get_billing_email();
//var_dump($email);
mailchecker($email);
}
}
function mailchecker($test_mail){
$life = array();
array_push($life, $test_mail);
var_dump($life);
}
I was trying to push the emails to an empty array but it only shows the current order email. I know I am doing something dumb here but I am stuck. Any kind soul wanna help me with this?
You Should try this:
add_action( 'admin_init' , 'custom_orders_list_column_content', 20, 3 ); // change accordingly
function custom_orders_list_column_content()
{
$billing_email = $skip_duplicate = array();
$customer_orders = get_posts( array(
'post_type' => 'shop_order',
'post_status' =>'wc-completed', // change accordingly
'posts_per_page' => '-1'
) );
foreach( $customer_orders as $order){
$order_id = $order->ID;
if($order_id){
$order = wc_get_order( $order_id );
$bemail = $order->get_billing_email();
if(!in_array($bemail, $skip_duplicate)){
$billing_email[] = $bemail;
array_push($skip_duplicate,$bemail);
}
}
}
echo "<pre>";print_r($billing_email); exit;
}
You can change "post_status" and "add_action" events accordingly.
I Hope are you okay.
Today I have problems with I open order invoice pdf in admin after products list have components.
I Remove all components as well
I am using woocommerce-pdf-invoices-packing-slips plugin.
Below is my product URL :
https://www.pcguru.lt/produktas/amd-home-guru/
My Invoice PDF screenshot as well.
How can I remove it?
I Searching Google but couldn't Found Anything.
After Code Debugging Step by Step, That's are Found Successfully.
File Path:
/wp-content/plugins/woocommerce-pdf-invoices-packing-slips/includes/documents/abstract-wcpdf-order-document-methods.php
public function get_order_items() {
$items = $this->order->get_items();
$data_list = array();
if( sizeof( $items ) > 0 ) {
foreach ( $items as $item_id => $item ) {
//Find Child Product Of Composite Product
global $wpdb;
$post_id = $wpdb->get_row("SELECT meta_value FROM pcg_woocommerce_order_itemmeta WHERE (meta_key = 'wooco_ids' AND order_item_id = '". $item_id ."')");
if($post_id->meta_value !=''){
$main_id = str_replace('/1/', '', $post_id->meta_value);
$data_ids = explode(",",$main_id);
}
//END Custom Code
$data = array();
// Set the item_id
$data['item_id'] = $item_id;
// Set the id
$data['product_id'] = $item['product_id'];
$data['variation_id'] = $item['variation_id'];
// Compatibility: WooCommerce Composit Products uses a workaround for
// setting the order before the item name filter, so we run this first
if ( class_exists('WC_Composite_Products') ) {
$order_item_class = apply_filters( 'woocommerce_order_item_class', '', $item, $this->order );
}
// Set item name
$data['name'] = apply_filters( 'woocommerce_order_item_name', $item['name'], $item, false );
// Set item quantity
$data['quantity'] = $item['qty'];
//$data['product_desc'] = $item->get_product(); // Get the WC_Product objec
//echo '<pre>'; print_r($product); echo '</pre>'; die;
// Set the line total (=after discount)
$data['line_total'] = $this->format_price( $item['line_total'] );
$data['single_line_total'] = $this->format_price( $item['line_total'] / max( 1, abs( $item['qty'] ) ) );
$data['line_tax'] = $this->format_price( $item['line_tax'] );
$data['single_line_tax'] = $this->format_price( $item['line_tax'] / max( 1, abs( $item['qty'] ) ) );
$data['tax_rates'] = $this->get_tax_rate( $item, $this->order, false );
$data['calculated_tax_rates'] = $this->get_tax_rate( $item, $this->order, true );
// Set the line subtotal (=before discount)
$data['line_subtotal'] = $this->format_price( $item['line_subtotal'] );
$data['line_subtotal_tax'] = $this->format_price( $item['line_subtotal_tax'] );
$data['ex_price'] = $this->get_formatted_item_price( $item, 'total', 'excl' );
$data['price'] = $this->get_formatted_item_price( $item, 'total' );
$data['order_price'] = $this->order->get_formatted_line_subtotal( $item ); // formatted according to WC settings
// Calculate the single price with the same rules as the formatted line subtotal (!)
// = before discount
$data['ex_single_price'] = $this->get_formatted_item_price( $item, 'single', 'excl' );
$data['single_price'] = $this->get_formatted_item_price( $item, 'single' );
// Pass complete item array
$data['item'] = $item;
// Get the product to add more info
if ( is_callable( array( $item, 'get_product' ) ) ) {
$product = $item->get_product();
} else {
$product = $this->order->get_product_from_item( $item );
}
// Checking fo existance, thanks to MDesigner0
if( !empty( $product ) ) {
// Thumbnail (full img tag)
$data['thumbnail'] = $this->get_thumbnail( $product );
$data['get_parent_id'] = is_callable( array( $product, 'get_parent_id' ) ) ? $product->get_parent_id() : '';
// Set item SKU
$data['sku'] = is_callable( array( $product, 'get_sku' ) ) ? $product->get_sku() : '';
// Set item weight
$data['weight'] = is_callable( array( $product, 'get_weight' ) ) ? $product->get_weight() : '';
// Set item dimensions
$data['dimensions'] = $product instanceof \WC_Product ? WCX_Product::get_dimensions( $product ) : '';
// Pass complete product object
$data['product'] = $product;
} else {
$data['product'] = null;
}
// Set item meta
if (function_exists('wc_display_item_meta')) { // WC3.0+
$data['meta'] = wc_display_item_meta( $item, array(
'echo' => false,
) );
} else {
if ( version_compare( WOOCOMMERCE_VERSION, '2.4', '<' ) ) {
$meta = new \WC_Order_Item_Meta( $item['item_meta'], $product );
} else { // pass complete item for WC2.4+
$meta = new \WC_Order_Item_Meta( $item, $product );
}
$data['meta'] = $meta->display( false, true );
}
if (!in_array($data['product_id'], $data_ids)) {
$data_list[$item_id] = apply_filters( 'wpo_wcpdf_order_item_data', $data, $this->order, $this->get_type());
}
}
}
//echo '<pre>'; print_r($data_list); echo '</pre>'; die;
return apply_filters( 'wpo_wcpdf_order_items_data', $data_list, $this->order, $this->get_type() );
}
Solution Screenshot:
I am very happy because solution are completed.
Also Thanks to all helping guys
In WooCommerce, with this code, i'm using product custom fields to add some custom metadata to order items when making a purchase. I also need to auto add some of this custom metadata to the customer Order Notes (basically so it gets sent to my shipping fulfillment service).
Below is the code i'm currently using:
add_action('woocommerce_checkout_create_order', 'before_checkout_create_order', 20, 2);
function before_checkout_create_order( $order, $data ) {
$items_in_order=array();
foreach( $order->get_items() as $item_id => $item ) {
$product = $order->get_product_from_item( $item );
$text = $product->get_sku();
$bags_count = array(
'neutral_bags' => 0,
'bright_bags' => 0,
'pastel_bags' => 0,
'reg_tendons' => 0,
'gid_tendons' => 0
);
$meta_key1 = __('Neutral Bags', 'woocommerce');
$meta_key2 = __('Bright Bags', 'woocommerce');
$meta_key3 = __('Pastel Bags', 'woocommerce');
$meta_key4 = __('Reg Tendons', 'woocommerce');
$meta_key5 = __('GID Tendons', 'woocommerce');
$bags_count['neutral_bags'] += wc_get_order_item_meta( $item_id, $meta_key1, true );
$bags_count['bright_bags'] += wc_get_order_item_meta( $item_id, $meta_key2, true );
$bags_count['pastel_bags'] += wc_get_order_item_meta( $item_id, $meta_key3, true );
$bags_count['reg_tendons'] += wc_get_order_item_meta( $item_id, $meta_key4, true );
$bags_count['gid_tendons'] += wc_get_order_item_meta( $item_id, $meta_key5, true );
$text .= ' | ' . $bags_count['neutral_bags'];
$text .= ' | ' . $bags_count['bright_bags'];
$text .= ' | ' . $bags_count['pastel_bags'];
$text .= ' | ' . $bags_count['reg_tendons'];
$text .= ' | ' . $bags_count['gid_tendons'];
array_push( $items_in_order , $text );
}
$text = implode('<br/>', $items_in_order );
// get customer note
$note = $order->get_customer_note();
// Merging existing customer note with bags count (if it exist)
$note = empty( $note ) ? $text : $note . '<br/>' . $text . ' V1';
// Set the new customer note before saving
$order->set_customer_note( $note );
}
As per the below screenshot, you'll see the data is saved to the product, but doesn't get pulled into the order notes section correctly.
Any ideas why this might be?
As this is related to your other question and my answer there, so the meta keys are a bit different:
neutral_bag_count meta key is replaced by: Neutral Bags,
bright_bag_count meta key is replaced by: Bright Bags.
Update: I have revisited completely all your existing code, avoiding repetitions, making it more effective and compact.
I have set your custom fields keys and labels in a function that I call in all other hooked functions and I use foreach loops everywhere now. If you want to change a label, you will do it just once in this utility function.
The new code:
// Utility function (keys + translatable labels)
function bags_and_sheets_labels_keys(){
$domain = 'woocommerce';
return array(
'neutral_bag' => __('Neutral Bags', $domain),
'bright_bag' => __('Bright Bags', $domain),
'pastel_bag' => __('Pastel Bags', $domain),
'reg_tendon' => __('Regular Tendon Sheets', $domain),
'gid_tendon' => __('GID Tendon Sheets', $domain),
);
}
// Add custom fields to single product pages
add_action( 'woocommerce_before_add_to_cart_button', 'add_custom_fields_single_product', 20 );
function add_custom_fields_single_product(){
global $product;
$bags = bags_and_sheets_labels_keys();
?>
<div class="product-custom-fields">
<?php foreach($bags as $key => $value ): ?>
<input type="text" placeholder="<?php echo $value; ?>" name="<?php echo $key; ?>">
<?php endforeach; ?>
</div>
<div class="clear"></div>
<?php
}
// Save custom fields to cart object
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_fields_cart_item_data', 10, 2 );
function save_custom_fields_cart_item_data( $cart_item_data, $product_id ){
$bags = bags_and_sheets_labels_keys();
foreach($bags as $key => $value ):
if(isset($_POST[$key]))
$cart_item_data['custom_data'][$key] = sanitize_text_field($_POST[$key]);
endforeach;
$cart_item_data['custom_data']['unique_key'] = md5( microtime().rand() );
WC()->session->set( 'custom_data', $cart_item_data['custom_data'] );
return $cart_item_data;
}
// Display custom fields in cart items (cart and checkout pages)
add_filter( 'woocommerce_get_item_data', 'display_custom_fields_cart_item_data', 10, 2 );
function display_custom_fields_cart_item_data($item_data, $cart_item){
if( ! array_key_exists( 'custom_data', $cart_item ) )
return $item_data;
$bags = bags_and_sheets_labels_keys();
foreach($bags as $key => $value ):
if( array_key_exists( $key, $cart_item['custom_data'] ) )
$item_data[] = array(
'key' => $value,
'value' => $cart_item['custom_data'][$key]
);
endforeach;
return $item_data;
}
// Save and display custom fields in order item meta
add_action( 'woocommerce_add_order_item_meta', 'add_custom_fields_order_item_meta', 20, 3 );
function add_custom_fields_order_item_meta( $item_id, $cart_item, $cart_item_key ) {
$bags = bags_and_sheets_labels_keys();
foreach($bags as $key => $meta_key_label ):
if( array_key_exists($key, $cart_item['custom_data']) )
wc_update_order_item_meta( $item_id, $meta_key_label, $cart_item['custom_data'][$key] );
endforeach;
}
// Add products SKUs and cutom fields as formated data in customer note
add_action('woocommerce_checkout_update_order_meta', 'after_checkout_create_order', 20, 2);
function after_checkout_create_order( $order_id, $data ) {
$skus = $counts = $output = array();
$bags = bags_and_sheets_labels_keys();
// Get the WC_Order Object (instance)
$order = wc_get_order($order_id);
// Loop through order items
foreach( $order->get_items() as $item_id => $item ) {
$product = $item->get_product();
$skus[] = $product->get_sku(); // Set SKUs in an array
// Add metakeys to the array (if they exist)
foreach( $bags as $meta_key_label ){
$item_meta = wc_get_order_item_meta( $item_id, $meta_key_label, true );
if( ! empty($item_meta) ){
$counts[$meta_key_label] += $item_meta;
$count = true;
}
}
}
// Add Products SKUs
$text = __('Products SKUs', 'woocommerce') . ': ' . implode(' - ', $skus ). ' | ';
// Get each custom item meta data count (if they exist)
if( isset($count)){
foreach( $counts as $key_label => $value ){
$output[] = $key_label . __(' count: ', 'woocommerce') . $value;
}
// Format all data as a text string
$text .= implode(' - ', $output );
}
// Get customer note
$note = $order->get_customer_note();
// Merging existing customer note with bags count (if it exist)
$note = empty( $note ) ? $text : $note . '<br/>' . $text;
// Set the new customer note before saving
$order->set_customer_note( $note );
$order->save();
}
Code goes in function.php file of the active child theme (or active theme).
Tested and works as expected.