With WooCommerce, I am trying to change the download URLs for order items. Originally I was using the order ID but that only allows one subscription per order. So I need the customer to be able to purchase more than one subscription.
Here is the code I currently have:
if ( !class_exists( 'pl_wcdf_extention' ) ) {
class pl_wcdf_extention {
public function __construct() {
add_filter( 'woocommerce_order_get_downloadable_items', array( $this, 'download_link' ), 10, 2 );
add_filter( 'woocommerce_customer_get_downloadable_products', array( $this, 'download_link' ), 10, 1 );
}
public function download_link( $downloads, $order = null ) {
$download_url_base = 'https://xxxx.com/';
// retrieve Order ID from Download array
$order_id = $downloads [0]['order_id'];
// retrieve Order data
$order = wc_get_order( $order_id );
// retrieve Order Items
$order_item = $order->get_items();
// retrieve first Order Item ID
$order_item_id = $order_item [0][order_item_id];
foreach ( $downloads as $download ) {
$download['download_url'] = $download_url_base . "xxxx-" . $order_item_id . ".svg";
$tag_item = array(
'--native-libs-dir' => '/home/Lib/',
'--type' => 'template0001style1',
'--data'=> $order_item_id,
'--join' => 'horizontal',
'--output-file' => '/home/public_html/xxxx-' . $order_item_id . '.svg'
);
$args = "";
foreach ($tag_item as $k=>$v) {
$args .= " $k " . " $v ";
}
shell_exec("/home/Python-2.7.14/Python-3.6.3/python /home/Lib/generate-code.py $args");
}
return $downloads;
}
}
}
if ( class_exists( 'pl_wcdf_extention' ) ) {
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
global $wpdb;
define( 'PL_WCDFE_DIR_PATH', plugin_dir_path( __FILE__ ) );
define( 'PL_WCDFE_PLUGIN_FILE', __FILE__ );
new pl_wcdf_extention();
}
Everything works except I am not able to get the order item ID.
Any help would be appreciated.
To retrieve order item id from the WC_Order_Item Object, please use the get_id() method.
So in your script, you should replace this section:
// retrieve first Order Item ID
$order_item_id = $order_item [0][order_item_id];
with this:
// retrieve first Order Item ID
$order_item_id = $order_item[0]->get_id();
Related
I'm trying to show stock remaining per product in a custom column on WooCommerce admin orders list, without success.
I would like to show it next to 'quantity'.
Ej. Product A x 1 (3)
My code attempt:
add_filter('manage_edit-shop_order_columns', 'new_order_items_column' );
function new_order_items_column( $order_columns ) {
$order_columns['order_products'] = "Productos";
return $order_columns;
}
add_action( 'manage_shop_order_posts_custom_column' , 'new_order_items_column_cnt' );
function new_order_items_column_cnt( $colname ) {
global $the_order; // the global order object
if( $colname == 'order_products' ) {
// get items from the order global object
$order_items = $the_order->get_items();
if ( !is_wp_error( $order_items ) ) {
foreach( $order_items as $order_item ) {
echo '▪️ ' . $order_item['name'] .' × '. $order_item['quantity'] .'<br />';
}
}
}
}
Any advice?
You can use get_stock_quantity(), note that products with variations and products that do not contain stock are also taken into account
So you get:
// Add a Header
function filter_manage_edit_shop_order_columns( $columns ) {
// Add new column
$columns['order_products'] = __( 'Products', 'woocommerce' );
return $columns;
}
add_filter( 'manage_edit-shop_order_columns', 'filter_manage_edit_shop_order_columns', 10, 1 );
// Populate the Column
function action_manage_shop_order_posts_custom_column( $column, $post_id ) {
// Compare
if ( $column == 'order_products' ) {
// Get an instance of the WC_Order object from an Order ID
$order = wc_get_order( $post_id );
// Is a WC_Order
if ( is_a( $order, 'WC_Order' ) ) {
foreach( $order->get_items() as $item ) {
// Product ID
$product_id = $item->get_variation_id() > 0 ? $item->get_variation_id() : $item->get_product_id();
// Get product
$product = wc_get_product( $product_id );
// Get stock quantity
$get_stock_quantity = $product->get_stock_quantity();
// NOT empty
if ( ! empty ( $get_stock_quantity ) ) {
$stock_output = ' (' . $get_stock_quantity . ')';
} else {
$stock_output = '';
}
// Output
echo '▪ '. $item->get_name() . ' × ' . $item->get_quantity() . $stock_output . '<br />';
}
}
}
}
add_action( 'manage_shop_order_posts_custom_column' , 'action_manage_shop_order_posts_custom_column', 10, 2 );
We add vendor information in the admin order details for each order via:
WooCommerce show vendor store-name (Dokan) in admin order details overview
Now I also want to add this information also in the order preview. I found this answer
Display custom data on Woocommerce admin order preview
We change the hook to woocommerce_admin_order_preview_end but now when we want to open the preview nothing happend.
Do we have to adjust the whole code in order that it works for the order preview or why is our approach not working?
function action_woocommerce_admin_order_vendor_data( $order ) {
// Empty array
$shop_names = array();
// Output
echo '<strong>' . __( 'Vendor(s): ', 'woocommerce' ) . '</strong>';
// Loop through order items
foreach ( $order->get_items() as $item ) {
// Get product object
$product = $item->get_product();
// Author id
$author_id = $product->post->post_author;
// Shopname
$vendor = dokan()->vendor->get( $author_id );
$shop_name = $vendor->get_shop_name();
// OR JUST USE THIS FOR SHOPNAME
// Shop name
// $shop_name = dokan()->vendor->get( $author_id )->get_shop_name();
// NOT in array
if ( ! in_array( $shop_name, $shop_names ) ) {
// Push to array
$shop_names[] = $shop_name;
// Output
echo $shop_name . ', ';
}
}
}
add_action('woocommerce_admin_order_preview_end', 'action_woocommerce_admin_order_vendor_data', 10, 1 );
As already explained in the link you referred to (By LoicTheAztec). You can't get the order object as it's a template that loads specific data via Ajax and there is no arguments for woocommerce_admin_order_preview_start action hook
Instead the filter hook woocommerce_admin_order_preview_get_order_details will allow you first to add some custom data that you will be able to call and display it via woocommerce_admin_order_preview_start or woocommerce_admin_order_preview_end action hook
So you get:
// Add custom order meta data to make it accessible in order preview template
function filter_woocommerce_admin_order_preview_get_order_details( $data, $order ) {
// Empty array
$shop_names = array();
// Loop through order items
foreach ( $order->get_items() as $item ) {
// Get product object
$product = $item->get_product();
// Author id
$author_id = $product->post->post_author;
// Shopname
$vendor = dokan()->vendor->get( $author_id );
$shop_name = $vendor->get_shop_name();
// OR JUST USE THIS FOR SHOPNAME
// Shop name
// $shop_name = dokan()->vendor->get( $author_id )->get_shop_name();
// NOT in array
if ( ! in_array( $shop_name, $shop_names ) ) {
// Push to array
$shop_names[] = $shop_name;
}
}
// NOT empty
if ( ! empty ( $shop_names ) ) {
// Store the value in the data array
$data['shop_names'] = implode( '<br>', $shop_names );
}
return $data;
}
add_filter( 'woocommerce_admin_order_preview_get_order_details', 'filter_woocommerce_admin_order_preview_get_order_details', 10, 2 );
// Display custom values in order preview
function action_woocommerce_admin_order_preview_start() {
// Output
echo '<div class="wc-order-preview-wrapper">';
echo '<div class="wc-order-preview-shop-names" style="padding:1.5em 1.5em 0; box-sizing:border-box;">';
// H2
echo '<h2>' . __( 'Vendor(s)', 'woocommerce' ) . '</h2>';
// Call the stored value and display it
echo '{{{ data.shop_names }}}';
// Close
echo '</div></div>';
}
add_action( 'woocommerce_admin_order_preview_start', 'action_woocommerce_admin_order_preview_start', 10, 0 );
I have a custom table in my db and i need to insert the product_id, user_id & order information to that table on the time of subscription renewal payment using the hook "woocommerce_subscription_renewal_payment_complete". is that possible to implement this way? or anyone knows any other idea for the same??
i checked with the below code, and its not inserting into the table
function data_woocommerce_renewal( $subscription, $last_order ) {
if ( ! $subscription )
return;
$order_id = $subscription->get_parent_id();
$order = wc_get_order( $order_id );
$user_id = $subscription->get_user_id();
$order_items = $subscription->get_items();
// Loop through order items
foreach ( $order_items as $item_id => $item ) {
// To get the subscription variable product ID and simple subscription product ID
$product_id[] = $item->get_product_id();
}
// if( 'completed' === $order->get_status() ) {
$user_details = array(
'token' => 'ygfvygrffburb',
'true_or_false' => TRUE,
'product_id' => $product_id[0],
'customer_id' => $user_id
);
data_insert_test_data($user_details);
// }
}
add_action( 'woocommerce_subscription_renewal_payment_complete', 'data_woocommerce_renewal', 10, 1 );
Insertion function below
//insert test data
function data_insert_test_data($user_details) {
global $wpdb;
$table_name = $wpdb->prefix . "test_data";
$result = $wpdb->insert($table_name, $user_details);
if ($result) {
return true;
} else {
return false;
}
}
On the checkout page of my WooCommerce based site, users will have a list of shipping methods to choose from depending on what they are purchasing
This is the list I want to capture:
Things like
“Free Shipping” for orders over a certain price
“Freight Shipping” for certain items, and so on.
My goal, is to display ALL available methods for each specific order, and display it on the “Edit Order / Order Details” page in the Admin view.
A small feature that would help us be able to quickly identify what option people are choosing more often, depending on the choices that they have available.
This is what I have so far:
add_action( 'woocommerce_checkout_update_order_meta', 'save_available_shipping_methods' );
function save_available_shipping_methods( $order_id ) {
$shippingmethods = WC()->cart->get_shipping_methods();
update_post_meta( $order_id, '_shipping_methods', $shippingmethods );
}
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'get_available_shipping_methods', 10, 1 );
function get_available_shipping_methods($order){
$order = wc_get_order( $order_id );
if ( $order ) {
echo '<p><strong>'.__('Available Shipping Methods: ').'</strong> ' . get_post_meta($order->get_shipping_methods(), '_shipping_field_value', true ) . '</p>';
}
}
I'll attach a picture as well to maybe make things a little easier to follow.
This is where I would like to put it. The current hook places the field underneath the shipping address
In the woocommerce_checkout_update_order_meta hook, the available shipping packages are stored via update_post_meta
Comments with explanation added in the code
function action_woocommerce_checkout_update_order_meta( $order_id ) {
// Get shipping packages
$packages = WC()->shipping()->get_packages();
// Set array
$rate_labels = array();
// Loop trough packages
foreach ( $packages as $key => $package ) {
// Loop through package rates
foreach( $package['rates'] as $rate_id => $rate ) {
// Push to array
$rate_labels[] = $rate->get_label();
}
}
// NOT empty
if ( ! empty ( $rate_labels ) ) {
// Update post meta
update_post_meta( $order_id, '_available_shipping_methods', $rate_labels );
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'action_woocommerce_checkout_update_order_meta', 10, 1 );
// Display on the order edit page (backend)
function action_woocommerce_admin_order_data_after_shipping_address( $order ) {
// Get meta
$rate_labels = $order->get_meta( '_available_shipping_methods' );
// True
if ( $rate_labels ) {
// Loop trough rate labels
foreach( $rate_labels as $rate_label ) {
// Output
echo '<p>' . $rate_label . '</p>';
}
}
}
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'action_woocommerce_admin_order_data_after_shipping_address', 10, 1 );
In case anyone is wondering, this is the final result. It will loop through each order to find the available shipping methods at the time, along with how much it was quoted for.
// Capture the available shipping methods, and costs:
function action_woocommerce_checkout_update_order_meta( $order_id ) {
// Get shipping packages
$packages = WC()->shipping()->get_packages();
// Set array
$rate_labels = array();
$rate_costs = array();
// Loop trough packages
foreach ( $packages as $key => $package ) {
// Loop through package rates
foreach( $package['rates'] as $rate_id => $rate ) {
// Push to array
$rate_labels[] = $rate->get_label();
$rate_costs[] = $rate->get_cost();
}
}
// NOT empty
if ( ! empty ( $rate_labels ) ) {
// Update post meta
update_post_meta( $order_id, '_available_shipping_methods', $rate_labels );
update_post_meta( $order_id, '_available_shipping_method_cost', $rate_costs );
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'action_woocommerce_checkout_update_order_meta', 10, 1 );
// Make it display on the edit order page:
function action_woocommerce_admin_order_data_after_shipping_address( $order ) {
// Get meta
$rate_labels = $order->get_meta( '_available_shipping_methods' );
$rate_costs = $order->get_meta( '_available_shipping_method_cost' );
$methods = array ( $rate_labels, $rate_costs );
// True
if ( $rate_labels ) {
// Loop
echo '<p><strong>Shipping Methods: </strong>';
foreach(array_combine($rate_labels, $rate_costs) as $rate_label => $rate_cost) {
echo '<p>' . $rate_label . ' - $' . $rate_cost . '</p>';
}
}
}
add_action( 'woocommerce_admin_order_data_after_shipping_address', 'action_woocommerce_admin_order_data_after_shipping_address', 10, 1 );
I have implemented a custom HTML Form and asking for some data which my customers will pass to place order successfully. Without these details my order has no importance.
For HTML form, I am referencing some custom PHP script which is below and which processes POST data from the Form and creates Cart with these data programmatically. Thanks #LoicTheAztec to help me achieve this.
The script.php file code:
<?php
require_once("../wp-load.php");
$customer_name = $_POST["customer_name"];
$customer_email = $_POST["customer_email"];
$customer_sex = $_POST["customer_sex"];
$customer_age = $_POST["customer_age"];
$product_id = $_POST["product_id"];
$custom_data = array(); // Initializing
if( isset($_POST['customer_name']) && ! empty($_POST['customer_name']) )
$custom_data['custom_data']['name'] = $_POST['customer_name'];
if( isset($_POST['customer_email']) && ! empty($_POST['customer_email']) )
$custom_data['custom_data']['email'] = $_POST['customer_email'];
if( isset($_POST['customer_sex']) && ! empty($_POST['customer_sex']) )
$custom_data['custom_data']['sex'] = $_POST['customer_sex'];
if( isset($_POST['customer_age']) && ! empty($_POST['customer_age']) )
$custom_data['custom_data']['age'] = $_POST['customer_age'];
global $woocommerce;
if (WC()->cart->add_to_cart( $product_id, '1', '0', array(), $custom_data )) {
var_dump($product_id);
} else {
var_dump($customer_name);
}
header("Location: ./checkout");
?>
As you see we have programmatically created a cart item using WC_Cart add_to_cart() method. So, all custom data is being saved to Cart as custom cart item data.
And now, we have also placed a code block into functions.php to print these data on Checkout page.
add_action( 'woocommerce_after_order_notes', 'my_custom_checkout_field' );
function my_custom_checkout_field( $checkout ) {
//var_dump($checkout);
global $woocommerce;
echo '<div id="my_custom_checkout_field"><h2>' . __('Child Info') . '</h2>';
foreach ( $woocommerce->cart->get_cart() as $cart_item ) {
if( isset($cart_item['custom_data']) ) {
$custom_data = $cart_item['custom_data'];
echo("<div>Name: <strong>" . $custom_data['name'] . "</strong></div>");
echo("<div>Email: <strong>" . $custom_data['email'] . "</strong></div>");
echo("<div>Gender: <strong>" . $custom_data['sex'] . "</strong></div>");
echo("<div>Age: <strong>" . $custom_data['age'] . "</strong></div>");
}
}
echo '</div>';
}
Now, I am trying to add these data printed on Checkout page to the Order page as well. As my order can't be completed without these data, user need to fill up these data and when he creates order, these data needs to be passed to the Order Summary page as well. And admin also needs to be able to see these data so he can process the order.
I hope this description clears everything and thanks again #LoicTheAztec to make me able to do this. Thank you very much.
Update 2 - Two steps
1) Saving data:
We will save this custom customer data as "Hidden" order "item" meta data and then as order meta data too as this is related to subscriptions with a unique order item:
// Utility function: array of custom customer key/label pairs
function custom_data_keys_labels(){
return array(
'name' => __('Customer name'), 'email' => __('Customer email'),
'sex' => __('Customer gender'), 'age' => __('Customer age'),
);
}
// Add/save custom field value as custom HIDDEN order item meta data
add_action( 'woocommerce_checkout_create_order_line_item', 'custom_field_update_order_item_meta', 20, 4 );
function custom_field_update_order_item_meta( $item, $cart_item_key, $values, $order ) {
if ( ! isset( $values['custom_data'] ) )
return;
$custom_data = $values['custom_data'];
$meta_data = array();
$labels_keys = custom_data_keys_labels();
foreach( $labels_keys as $key => $label ){
if ( isset( $custom_data[$key] ) )
$meta_data[$key] = $custom_data[$key];
}
if ( sizeof( $meta_data ) > 0 )
$item->update_meta_data( __('_customer_data'), $meta_data );
return $cart_item_data;
}
// Add/save custom fields values as custom order meta data
add_action( 'woocommerce_checkout_create_order', 'my_custom_checkout_field_update_order_meta', 20, 2 );
function my_custom_checkout_field_update_order_meta( $order, $data ) {
$order_items = $order->get_items(); // Order itesm
$item = reset($order_items); // Keep only the first order item
$item_data = $item->get_meta( '_customer_data' ); // Get custom customer data
if( is_array($item_data) && sizeof($item_data) > 0 ){
foreach( $item_data as $key => $value ) {
if ( isset( $item_data[$key] ) )
$order->update_meta_data( '_customer_' . $key, $value );
}
// Mark as data saved
$order->update_meta_data( '_customer_data_set', true );
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
2) Displaying saved custom data:
The code below also use our utility function custom_data_keys_labels()…
// Order pages (frontend and admin) display
add_filter( 'woocommerce_order_details_after_order_table' , 'display_admin_order_meta_cutom_data', 20, 1 ); // Front
add_action( 'woocommerce_admin_order_data_after_billing_address', 'display_admin_order_meta_cutom_data', 20, 1 ); // Admin
function display_admin_order_meta_cutom_data( $order ){
$labels_keys = custom_data_keys_labels();
if( $order->get_meta( '_customer_data_set' ) ){
if( is_admin() ){ // Admin
echo '<p>';
foreach( $labels_keys as $key => $label ){
if ( $order->get_meta( '_customer_' . $key ) )
echo '<strong>' . $label . ':</strong> ' . $order->get_meta( '_customer_' . $key ) . '<br>';
}
echo '</p>';
}
else { // Front end: order view and Order received (thankyou)
echo '<table class="woocommerce-table"><tbody>';
foreach( $labels_keys as $key => $label ){
if ( $order->get_meta( '_customer_' . $key ) )
echo '<tr><th>' . $label . ':</th><td>' . $order->get_meta( '_customer_' . $key ) . '</td></tr>';
}
echo '</tbody></table>';
}
}
}
// Email notifications display
add_filter( 'woocommerce_email_order_meta_fields' , 'display_email_cutom_data', 20, 3 );
function display_email_cutom_data ( $fields, $sent_to_admin, $order ) {
$labels_keys = custom_data_keys_labels();
if( $order->get_meta( '_customer_data_set' ) ){
foreach( $labels_keys as $key => $label ){
$fields['customer_' . $key] = array(
'label' => $label,
'value' => $order->get_meta( '_customer_' . $key ),
);
}
}
return $fields;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.