Woocommerce Get Vendor Information with Product ID - wordpress

I am using Woo Commerce with WC Vendor and WC Booking plugin. I want to send booking notification to vendor. Currently it sends notification to Customer and Administrator and when admin changes product status to processing & completed, then it sends notification to vendor. However, I want to send vendor notification along with admin notification.
I tried this hook:
add_action( 'woocommerce_new_booking', 'new_order_email_to_vendor', 10, 4 );
function new_order_email_to_vendor( $order ){
$emails = WC()->mailer()->get_emails();
if ( ! empty( $emails ) ) {
$emails['WC_Product_Vendors_Order_Email_To_Vendor']->trigger( $order );
}
}
But it throws an error:
Fatal error: Call to a member function get_date_created() on boolean in /wp-content/plugins/woocommerce-product-vendors/includes/emails/class-wc-product-vendors-order-email-to-vendor.php on line 56
So, now I am trying to hook vendor email directly along with customer and administrator email in this line:
$this->recipient = $this->get_option( 'recipient', get_option( 'admin_email' ), 'WILL ADD VENDOR EMAIL HERE' );
in file:
wp-content/plugins/woocommerce-bookings/includes/emails/class-wc-email-new-booking.php
At this stage, I have bookable product id available and I am trying to pull vendor information using product id but I don't found any information.
I tried:
get_post()
get_post_meta()
get_post_meta_by_id()
The Question is: How to get vendor information (specifically email) using product id?

I don't have and I have never used WC Vendors plugin as this is a non official commercial plugin (not made by automatic).
To get the vendor ID (after searching a bit) you can get it this way from a product ID:
$vendor_id = get_post_field( 'post_author', $product_id );
Now you can get the vendor email this way:
$vendor_id = get_post_field( 'post_author', $product_id );
$vendor = get_userdata( $vendor_id );
$email = $vendor->user_email;
May be a good turn around:
You can use the dedicated filter hook woocommerce_email_recipient_{$this->id} where $this->id is the ID of the notification type, for new_booking email ID (and also for testing new_order email ID too).
This will allow you to add additional email recipients.
In email notifications hooks, the Order object is nearly always defined. As In an order you can have many items (different products rom different vendors), you will need to get the vendor ID from each.
In the code below I add to the recipients the vendors emails for new_booking and new_order email notifications:
add_filter( 'woocommerce_email_recipient_new_booking', 'additional_customer_email_recipient', 10, 2 );
add_filter( 'woocommerce_email_recipient_new_order', 'additional_customer_email_recipient', 10, 2 ); // Optional (testing)
function additional_customer_email_recipient( $recipient, $order ) {
if ( ! is_a( $order, 'WC_Order' ) ) return $recipient;
$additional_recipients = array(); // Initializing…
// Iterating though each order item
foreach( $order->get_items() as $item_id => $line_item ){
// Get the vendor ID
$vendor_id = get_post_field( 'post_author', $line_item->get_product_id());
$vendor = get_userdata( $vendor_id );
$email = $vendor->user_email;
// Avoiding duplicates (if many items with many emails)
// or an existing email in the recipient
if( ! in_array( $email, $additional_recipients ) && strpos( $recipient, $email ) === false )
$additional_recipients[] = $email;
}
// Convert the array in a coma separated string
$additional_recipients = implode( ',', $additional_recipients);
// If an additional recipient exist, we add it
if( count($additional_recipients) > 0 )
$recipient .= ','.$additional_recipients;
return $recipient;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This should work without errors.

Related

Redirecting customers to custom page after sale in WooCommerce

I'm trying to learn WooCommerce and WordPress plugins so I'm tweaking around. I'm trying to create a plugin that redirects customer to a custom page after checkout. The custom page/url can be defined when I create the product. Here is my code:
<?php
/*
Plugin Name: Custom Redirect After Sale
Description: Redirects customers to a custom page after a successful sale.
*/
// Register a new meta field for products
add_action( 'add_meta_boxes', 'custom_redirect_meta_box' );
function custom_redirect_meta_box() {
add_meta_box( 'custom_redirect_meta_box', 'Custom Redirect URL', 'custom_redirect_meta_box_callback', 'product', 'side' );
}
function custom_redirect_meta_box_callback( $post ) {
$value = get_post_meta( $post->ID, '_custom_redirect_url', true );
echo '<label for="custom_redirect_url">Custom Redirect URL:</label>';
echo '<input type="text" id="custom_redirect_url" name="custom_redirect_url" value="' . esc_attr( $value ) . '" style="width:100%">';
}
// Save the meta field value when the product is saved
add_action( 'save_post_product', 'save_custom_redirect_meta_box_data' );
function save_custom_redirect_meta_box_data( $post_id ) {
if ( isset( $_POST['custom_redirect_url'] ) ) {
update_post_meta( $post_id, '_custom_redirect_url', sanitize_text_field( $_POST['custom_redirect_url'] ) );
}
}
// Redirect to the custom page after a successful sale
add_action( 'woocommerce_payment_complete', 'custom_redirect_after_sale' );
function custom_redirect_after_sale( $order_id ) {
$order = wc_get_order( $order_id );
//$order->update_status( 'completed' );
$items = $order->get_items();
// Get the first product in the order
$product = reset($items);
// Get the custom redirect URL for the product
//$redirect_url = get_post_meta( $product->get_product_id(), '_custom_redirect_url', true );
$redirect_url = get_post_meta( $product->get_id(), '_custom_redirect_url', true );
//echo "Meta retrieved: " . $redirect_url;
//error_log("callback fired");
//echo "Payment complete ho ho ho";
if( $redirect_url ) {
wp_redirect( $redirect_url );
exit;
}
}
It seems the woocommerce_payment_complete hook is not firing. I tried to echo out the redirect url and text but it doesn't seem to work.
I'm on localhost and I'm using the cash on delivery payment method.
Basing this answer on the great https://rudrastyh.com/ - specifically this tutorial https://rudrastyh.com/woocommerce/thank-you-page.html#redirects this is the code that should work for what you are trying to do.
First, you hook into the template_redirect action to determine the URL where the customer needs to go
Getting the Order ID, you can get the products purchased for that order
Once you have the purchased products, you can get their ID and meta data, the redirect URL you saved for each. Note that while you use WP functions for handling meta, when working with WooCommerce it is best practice to use its CRUD methods. In case in the future they port products to custom tables, your code will continue working.
Implement the redirect with the WP function wp_safe_redirect
Note that what you are trying to achieve will have problems if customers purchase orders with more than 1 product, and you have more than 1 redirect URL. In this implementation, the first product in the order that has a saved redirect URL will override all others
add_action( 'template_redirect', 'purchased_product_redirect');
function purchased_product_redirect(){
if( !function_exists( 'is_wc_endpoint_url' )){
return;
}
// do nothing if we are not on the order received page
if( ! is_wc_endpoint_url( 'order-received' ) || empty( $_GET[ 'key' ] ) ) {
return;
}
// Get the order ID
$order_id = wc_get_order_id_by_order_key( $_GET[ 'key' ] );
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
// Get and Loop Over Order Items
foreach ( $order->get_items() as $item_id => $item ) {
$product_id = $item->get_product_id();
$product = wc_get_product($product_id);
if(!$product){
continue;
}
//Get the first product's redirect URL
$product_redirect_url = $product->get_meta('_custom_redirect_url');
if(!$product_redirect_url){
continue;
}
wp_safe_redirect( $product_redirect_url );
exit; // always exit after using wp_safe_redirect
}
}

Hide item meta data in certain WooCommerce email notifications

I've got this code snippet in my functions.php file:
add_action( 'woocommerce_checkout_create_order_line_item', 'add_custom_field_to_order_item_meta', 10, 4 );
function add_custom_field_to_order_item_meta( $item, $cart_item_key, $values, $order ) {
$custom_field_value = get_post_meta( $item->get_product_id(), 'supplier_sku', true );
if ( ! empty($custom_field_value) ){
$item->update_meta_data( __('Supplier SKU', 'woocommerce'), $custom_field_value );
}
}
It pulls in the custom field on products, called Supplier SKU and then adds it to the WooCommerce email notifications. Which is fine, but I want to exclude it from the customer email notification and only have it display in the admin email notification.
How can I achieve this?
You could use the woocommerce_display_item_meta hook and return an empty string
function filter_woocommerce_display_item_meta ( $html, $item, $args ) {
$html = '';
return $html;
}
add_filter( 'woocommerce_display_item_meta', 'filter_woocommerce_display_item_meta', 10, 3 );
While the above would work, there would be some issues, namely:
The hook doesn't run just for email notifications, so it wouldn't show up anywhere
Even if this hook would only be executed for email notifications, we would still need to specify that this should only be the case for certain email notifications. However, this hook does not offer a solution for it by default to make this distinction
So a workaround will be needed, this can be done by creating a global variable through another hook that applies only to email notifications
Step 1) creating and adding a global variable
// Setting global variable
function action_woocommerce_email_before_order_table( $order, $sent_to_admin, $plain_text, $email ) {
$GLOBALS['email_id'] = $email->id;
}
add_action( 'woocommerce_email_before_order_table', 'action_woocommerce_email_before_order_table', 1, 4 );
Step 2) In the hook woocommerce_display_item_meta, add and check for specific conditions
Only for email notifications
Only for specific meta data
Only for admin 'new order' email
function filter_woocommerce_display_item_meta ( $html, $item, $args ) {
// For email notifications and specific meta
if ( ! is_wc_endpoint_url() && $item->is_type( 'line_item' ) && $item->get_meta( 'Supplier SKU' ) ) {
// Getting the email ID global variable
$ref_name_globals_var = isset( $GLOBALS ) ? $GLOBALS : '';
$email_id = isset( $ref_name_globals_var['email_id'] ) ? $ref_name_globals_var['email_id'] : '';
// NOT empty and targeting specific email. Multiple statuses can be added, separated by a comma
if ( ! empty ( $email_id ) && ! in_array( $email_id, array( 'new_order' ) ) ) {
$html = '';
}
}
return $html;
}
add_filter( 'woocommerce_display_item_meta', 'filter_woocommerce_display_item_meta', 10, 3 );

How to display the last ordered product in WooCommerce via a shortcode

I'm looking for a way to display the last ordered product on another page.
I think it would be possible to maybe create a shortcode in the functions that takes the order details and displays them wherever I add the shortcode.
But I can't seem to figure out how to get it to work. So far I got this information to work with:
add_shortcode( 'displaylast', 'last' );
function last(){
$customer_id = get_current_user_id();
$order = wc_get_customer_last_order( $customer_id );
return $order->get_order();
}
[displaylast] is currently showing me noting. It does work when I change get_order() to get_billing_first_name().
That displays the order name. But I can't seem to get the item that was bought. Maybe there is a get_() that I'm not seeing?
You are close, however you must obtain the last product from the order object.
So you get:
function last() {
// Not available
$na = __( 'N/A', 'woocommerce' );
// For logged in users only
if ( ! is_user_logged_in() ) return $na;
// The current user ID
$user_id = get_current_user_id();
// Get the WC_Customer instance Object for the current user
$customer = new WC_Customer( $user_id );
// Get the last WC_Order Object instance from current customer
$last_order = $customer->get_last_order();
// When empty
if ( empty ( $last_order ) ) return $na;
// Get order items
$order_items = $last_order->get_items();
// Latest WC_Order_Item_Product Object instance
$last_item = end( $order_items );
// Get product ID
$product_id = $last_item->get_variation_id() > 0 ? $last_item->get_variation_id() : $last_item->get_product_id();
// Pass product ID to products shortcode
return do_shortcode("[product id='$product_id']");
}
// Register shortcode
add_shortcode( 'display_last', 'last' );
SHORTCODE USAGE
In an existing page:
[display_last]
Or in PHP:
echo do_shortcode('[display_last]');

WooCommerce Multivendor Marketplace get vendor id from product id

I am using WooCommerce Multivendor Marketplace plugin in Wordpress for multivendor site. I want to restrict a user so they can add only one vendor's product in their cart. I am writing a custom function to get vendor id.
I'm trying to use the get_wcfm_product_vendors function but it isn't working; I think that it might not be supported. I've search for other possible solutions but I could not find any. Is there any other method I can use to get vendor id for a product?
You may try WCFM single vendor checkout function, add this code to your child theme :
add_action( 'woocommerce_add_to_cart_validation', function( $is_allow, $product_id, $quantity ) {
$product = get_post( $product_id );
$product_author = $product->post_author;
//Iterating through each cart item
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$cart_product_id = $cart_item['product_id'];
$cart_product = get_post( $cart_product_id );
$cart_product_author = $cart_product->post_author;
if( $cart_product_author != $product_author ) {
$is_allow = false;
break;
}
}
if( !$is_allow ){
// We display an error message
wc_clear_notices();
wc_add_notice( __( "Well, you already have some item in your cart. First checkout with those and then purchase other items!", "wcfm" ), 'error' );
}
return $is_allow;
}, 50, 3 );
https://docs.wclovers.com/tweaks/#single-vendor-checkout

Woocommerce REST API v2: Issue with adding custom data to orders

I have a site running an old version of Woo v2.5.5 using legacy v3 for the API. I was able to use the action woocommerce_api_order_response to add data to the orders.
Something like:
add_action( 'woocommerce_api_order_response', 'add_testing_api_function', 10, 1 );
function add_testing_api_function( $order_data ) {
$order_data['foo'] = "testing";
return $order_data;
}
This works fine over the older API link:
https://example.com/wc-api/v3/orders?consumer_key=KEY&consumer_secret=SECRET
However, I need to update to Woo v3.3+ and the REST API is server up as:
https://example.com/wp-json/wc/v2/orders?consumer_key=KEY&consumer_secret=SECRET
My custom data no longer appears, and the hook does not appear to work. Is there another I can use?
WC API is indeed a great tool. Adding your own custom data to WC API shop order's response in WooCommerce 3.x can still be achieved as easily as it used to be with the legacy version of the API.
WooCommerce has these prepare filters for most of their API responses (see). Note that the format of them is woocommerce_rest_prepare_{$post_type}, where $post_type is a post type or taxonomy name like shop_orders or product_cat. In WooCommerce 2.7+ some of these filters also have a _object suffix.
As long as our intent is to add custom data to orders, the right filter to use will be woocommerce_rest_prepare_shop_order_object, where shop_order is our {$post_type} followed the _object suffix (as described above).
The following function conditionally gets user meta and returns the user's social profile's avatar url if available.
/**
* Add custom data to WC API shop order response
* Overriding "$object" here with $order so it's easier to access its properties
*/
function my_wc_rest_prepare_order( $response, $order, $request ) {
if( empty( $response->data ) )
return $response;
$order_id = $order->get_id();
// Get an instance of the WC_Order object
$order = wc_get_order($order_id);
// Get the user ID from WC_Order methods
$user_id = $order->get_customer_id(); // $order->get_user_id(); // or $order->get_customer_id();
// check for WooCommerce Social Login User Avatar
if( class_exists( 'WC_Social_Login' ) ) {
$fb_avatar = get_user_meta( $user_id, '_wc_social_login_facebook_profile_image', true );
$gplus_avatar = get_user_meta( $user_id, '_wc_social_login_google_profile_image', true );
}
$social_data = array();
$avatar_url = array();
$customer_picture = array(
'default' => get_avatar_url( $user_id ),
'facebook' => ( $fb_avatar ) ? esc_url( $fb_avatar ) : '',
'google' => ( $gplus_avatar ) ? esc_url( $gplus_avatar ) : ''
);
$response->data['social_data']['avatar_url'] = $customer_picture;
return $response;
}
add_filter( 'woocommerce_rest_prepare_shop_order_object', 'my_wc_rest_prepare_order', 10, 3 );
Result:
[{
"social_data": {
"avatar_url": {
"default": "https://secure.gravatar.com/avatar/6e27402273b47316097247a2057492f8?s=96&d=mm&r=g",
"facebook": "https://graph.facebook.com/2028542057604385/picture?width=150&height=150",
"google": ""
}
},
}]

Resources