How to add or link purchased product to customer - woocommerce

I am doing migration from very old custom made ecommerce site that is working as subscription model. I have 3 different products that contains custom data to is available to customers that have purchased to product.
So i am importing customers by their email address and i need to add products to their purchase / order history so the can get their hands on into custom data.
So how to link a product to customer?

I'm not so sure about those custom fields but it seem like what to need is to create orders programmatically, where you "link" existing users and existing products.
Luckily, WooCommerce allow us to do that :)
take a look at this code:
function create_order() {
// Create product
$product = WC_Helper_Product::create_simple_product();
WC_Helper_Shipping::create_simple_flat_rate();
$order_data = array('status' => 'pending', 'customer_id' => 1, 'customer_note' => '', 'total' => '');
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
// Required, else wc_create_order throws an exception
$order = wc_create_order($order_data);
// Add order products
$item_id = $order->add_product($product, 4);
// Set billing address
$billing_address = array('country' => 'US', 'first_name' => 'Jeroen', 'last_name' => 'Sormani', 'company' => 'WooCompany', 'address_1' => 'WooAddress', 'address_2' => '', 'postcode' => '123456', 'city' => 'WooCity', 'state' => 'NY', 'email' => 'admin#example.org', 'phone' => '555-32123');
$order->set_address($billing_address, 'billing');
// Add shipping costs
$shipping_taxes = WC_Tax::calc_shipping_tax('10', WC_Tax::get_shipping_tax_rates());
$order->add_shipping(new WC_Shipping_Rate('flat_rate_shipping', 'Flat rate shipping', '10', $shipping_taxes, 'flat_rate'));
// Set payment gateway
$payment_gateways = WC()->payment_gateways->payment_gateways();
$order->set_payment_method($payment_gateways['bacs']);
// Set totals
$order->set_total(10, 'shipping');
$order->set_total(0, 'cart_discount');
$order->set_total(0, 'cart_discount_tax');
$order->set_total(0, 'tax');
$order->set_total(0, 'shipping_tax');
$order->set_total(40, 'total');
// 4 x $10 simple helper product
return wc_get_order($order->id);
}
at the 5th line, you set the customer ID
and, at the 12th line, you assign the product to the order.
caveat: I'm not sure how much of this snippet is required by wc_create_order(), as the official documentation is very poor.
I'd try to run this function as clean as possible, just using the minimum information (or the information that you have available).
Something like this:
function create_order() {
// Create product
$product = WC_Helper_Product::create_simple_product();
$order_data = array('status' => 'pending', 'customer_id' => 1, 'customer_note' => '', 'total' => '');
// Required, else wc_create_order throws an exception
$order = wc_create_order($order_data);
// Add order products
$item_id = $order->add_product($product, 4);
// Set totals
//$order->set_total(40, 'total');
return wc_get_order($order->id);
}
good luck!

Related

Add total orders and customer status in new order email notification in WooCommerce

I would like to add a note to the new order email notifications that the admin gets. This note should indicate whether the order is from a new customer, or from a returning customer.
This so that the people in the warehouse know what they should add in the shippingbox. We give new customers a ticket and don't want customers who order again to receive the same ticket, they get a diffrent ticket.
This should apply to both logged in users and users who are not logged in. A check via email address perhaps?
This code is using the has_bought for all customer orders - source
function has_bought() {
// Get all customer orders
$customer_orders = get_posts( array(
'numberposts' => 1, // one order is enough
'meta_key' => '_customer_user',
'meta_value' => get_current_user_id(),
'post_type' => 'shop_order', // WC orders post type
'post_status' => 'wc-completed', // Only orders with "completed" status
'fields' => 'ids', // Return Ids "completed"
) );
// return "true" when customer has already at least one order (false if not)
return count($customer_orders) > 0 ? true : false;
}
But I don't get it on the the new order email that admins receive, I would like to show it as:
Customer: new OR returning
Number of purchases: 4
How would i go about this and is this possible to create?
Some comments/suggestions:
To display text in email notifications, you can use different hooks, depending on where exactly you want to display the text. For example, the woocommerce_email_order_details hook is very suitable for this
To target a specific email notification you can use $email->id
Via wc_get_orders() you can get and count the existing orders for a customer based on the email address. If desired, pass a series of arguments that define the criteria for the search
So you get:
function action_woocommerce_email_order_details( $order, $sent_to_admin, $plain_text, $email ) {
// Target specific email notification
if ( $email->id == 'new_order' ) {
// Get email
$customer_email = $order->get_billing_email();
// NOT empty
if ( ! empty ( $customer_email ) ) {
// Get orders from customer by email and statuses
$orders_by_customer_email = wc_get_orders( array(
'customer' => $customer_email,
'status' => array( 'wc-on-hold','wc-processing','wc-completed' ),
'limit' => -1,
'return' => 'ids'
));
// When new customer
if ( count( $orders_by_customer_email ) == 1 ) {
$customer = __( 'new', 'woocommerce' );
} else {
$customer = __( 'returning', 'woocommerce' );
}
// Output
echo '<p style="color:red;font-size:14px;">' . sprintf( __( 'Customer: %s, number of purchases: %d', 'woocommerce' ), $customer, count( $orders_by_customer_email ) ) . '</p>';
}
}
}
add_action( 'woocommerce_email_order_details', 'action_woocommerce_email_order_details', 10, 4 );

WooCommerce Shipping Plugin - Checkout Shipping Cost Not Updating(AJAX)

I've searched to find any solution but nothing worked.
Here what I want to do:
Add new field in checkout page called Shipping show as selectbox filled with Preorder and Non-preorder
If preorder then would show Location field show as selectbox
If non-preorder then would show Location field same as step 2 but different list option.
When Shipping and Location filled correctly, shipping cost would show with correct shipping cost.
Problem
When I filled all required field and Shipping, Location shipping cost wound calculate again. It do AJAX request, but shipping cost keep the same.
But, when I change Name or Street Address shipping cost updated correctly, which is bad behavior. When user click Place Order immediately would POST wrong shipping cost.
Here my Youtube Video to make clear what I ask for help.
What I did to create my own plugin:
Create custom field using hook something like this
public function woocommerce_checkout_fields( $fields )
{
// var_dump(have_rows('location', 'option'));
// die('test');
$options = [];
$options[''] = 'Select Shipping';
$options['pre-order'] = 'Pre Order';
if (!$this->hasPreorderItem()) {
$options['non-pre-order'] = 'Non Pre Order';
}
// Add custom billing shipping
$fields['billing']['billing_shipping'] = array (
'type' => 'select',
'label' => 'Shipping',
'placeholder' => 'Select shipping',
'class' => array ('address-field', 'update_totals_on_change' ),
'required' => true,
'options' => $options
// 'options' => $this->getLocations()
);
$fields['billing']['billing_location_pre_order'] = array (
'type' => 'select',
'label' => 'Location(Pre Order)',
'placeholder' => 'Select Preorder Location',
'class' => array ('address-field', 'update_totals_on_change' ),
'required' => false,
'options' => $this->preorderLocations->getLocations()
// 'options' => $this->getLocations()
);
// Add custom billing location
$fields['billing']['billing_location'] = array (
'type' => 'select',
'label' => 'Location',
'placeholder' => 'Select location',
'class' => array ('address-field', 'update_totals_on_change' ),
'required' => false,
'options' => $this->locations->getLocations()
// 'options' => $this->getLocations()
);
return $fields;
}
Create class to help calculate cost based on Shipping and Location.
What I have tried
Using this answer but no luck. Click here for the question.
Tried to make field to required, as I saw someone said it only update when all required fields filled.
Tried to disable cache in wp-config.php
I found solution from this link
I might help someone that has similar problem. You just need to clear the session. So, WooCommerce would re-calculate and recall your calculate_shipping() function.
To do that add woocommerce_checkout_update_order_review hook. It would look something like this
add_action('woocommerce_checkout_update_order_review', 'action_woocommerce_checkout_update_order_review', 10, 1);
function action_woocommerce_checkout_update_order_review( $posted_data )
{
global $woocommerce;
$packages = $woocommerce->cart->get_shipping_packages();
foreach( $packages as $package_key => $package ) {
$session_key = 'shipping_for_package_'.$package_key;
$stored_rates = WC()->session->__unset( $session_key );
}
}

How do I split an WooCommerce order programmatically?

I sell packages online in my store. When a customer orders 2 packages (1 package consists of 4 products), I want to create 2 orders with 1 package, instead of 1 order with 2 packages.
Example:
I have a package (ID: 212), which can consist of up to 4 bundled products (all ID: 730). The problem right now: When I try to split the order, only the product with ID 212 is put into a new order. The 4 products, which belong to ID 212 still stay in the same order.
Following a screenshot, which makes the hierarchy more clear:
What happens, when I try the solution from #Shir Gans:
The package is put into a new order, but the products, which should also be in the new order are still in the old order.
What is the easiest way to do this, and which hook do I have to use?
You can hook to woocommerce_payment_complete which accepts $order_id, then you can loop through the items and decide if to create another order. Then you can split the items into a new order. here is an (untested) example:
define('PACKAGE_PRODUCT_ID', 1010);
add_action('woocommerce_payment_complete', 'order_splitter', 100, 1);
function order_splitter($order_id){
$completed_order = new WC_Order($order_id);
$item_splitted = false;
$address = array(
'first_name' => $completed_order->get_billing_first_name(),
'last_name' => $completed_order->get_billing_last_name(),
'company' => '',
'email' => $completed_order->get_billing_email(),
'phone' => $completed_order->get_billing_phone(),
'address_1' => $completed_order->get_billing_address_1(),
'address_2' => $completed_order->get_billing_address_2(),
'city' => $completed_order->get_billing_city(),
'state' => $completed_order->get_billing_state(),
'postcode' => $completed_order->get_billing_postcode(),
'country' => $completed_order->get_billing_country()
);
foreach($completed_order->get_items() as $item){
if (!$item_splitted && $item->get_product_id() === PACKAGE_PRODUCT_ID) {
//create new order
$new_order_args = array(
'customer_id' => $completed_order->get_customer_id(),
'status' => 'wc-pending',
);
$new_order = wc_create_order($new_order_args);
$product_to_add = wc_get_product(PACKAGE_PRODUCT_ID);
$new_order->add_product($product_to_add, 1, array());
$new_order->set_address($address, 'billing');
$new_order->set_address($address, 'shipping');
$new_order->update_status('wc-processing');
$new_order->add_order_note('This order created automatically');
$new_order->save();
$completed_order->remove_item($item->get_id());
$item_splitted = true;
} else if ($item_splitted && $item['product_id'] === PACKAGE_PRODUCT_ID){
# This will ensure every 2 products are splitted (skipping the 2nd one)
$item_splitted = false;
continue;
}
}
}

Woocommerce order formatted billing address reorder and custom billing field

Thanks to the filter "WooCommerce admin billing fields" I have ordered the billing fields in the notificiación footer by email but when I try to insert my custom billing field does not appear.
add_filter( 'woocommerce_order_formatted_billing_address' , 'woo_reorder_billing_fields', 10, 2 );
function woo_reorder_billing_fields( $address, $wc_order ) {
$address = array(
'first_name' => $wc_order->billing_first_name,
'last_name' => $wc_order->billing_last_name,
'company' => $wc_order->billing_company,
'my_field' => $wc_order->billing_my_field,
'country' => $wc_order->billing_country
);
return $address;
}
In order admin edit I can show my custom billing field thanks to the filter "woocommerce_admin_billing_fields", first adding the field and then reordering the Array.
I note that I added before and I have reordered this field in my checkout with the filter "woocommerce_checkout_fields".
Why not show my custom field if "$ wc_order" object stores the field in the checkout?
Any ideas?
Thanks in advance!
Yes, here the explanation:
We will register the new cusotm field, in this example the field "VAT" to store the fiscal document for companies in the European Union. There is a lot of documentation on how to register the custom fields and display them in the admin / user panel.
add_filter( 'woocommerce_default_address_fields', 'woo_new_default_address_fields' );
function woo_new_default_address_fields( $fields ) {
$fields['vat'] = array(
'label' => __( 'VAT number', 'woocommerce' ),
'class' => array( 'form-row-wide', 'update_totals_on_change' ),
);
return $fields;
}
Then add the new field "VAT" only to the registration of billing fields for mails, we do not want it to appear in the address fileds section.
add_filter( 'woocommerce_order_formatted_billing_address' , 'woo_custom_order_formatted_billing_address', 10, 2 );
function woo_custom_order_formatted_billing_address( $address, $WC_Order ) {
$address = array(
'first_name' => $WC_Order->billing_first_name,
'last_name' => $WC_Order->billing_last_name,
'vat' => $WC_Order->billing_vat,
'company' => $WC_Order->billing_company,
'address_1' => $WC_Order->billing_address_1,
'address_2' => $WC_Order->billing_address_2,
'city' => $WC_Order->billing_city,
'state' => $WC_Order->billing_state,
'postcode' => $WC_Order->billing_postcode,
'country' => $WC_Order->billing_country
);
return $address;
}
The following code customizes the appearance of the addresses, this is where we must add the call to the new VAT field. This allows us to customize the address view for each country independently.
add_filter( 'woocommerce_formatted_address_replacements', function( $replacements, $args ){
$replacements['{vat}'] = $args['vat'];
return $replacements;
}, 10, 2 );
add_filter( 'woocommerce_localisation_address_formats' , 'woo_includes_address_formats', 10, 1);
function woo_includes_address_formats($address_formats) {
$address_formats['ES'] = "{name}\n{company}\n{vat}\n{address_1}\n{address_2}\n{postcode} {city}\n{state}\n{country}";
$address_formats['default'] = "{name}\n{company}\n{vat}\n{nif}\n{address_1}\n{address_2}\n{city}\n{state}\n{postcode}\n{country}";
return $address_formats;
}
Any questions ask!

Filter shipping method based on custom checkout field at checkout page

I am using Woocommerce WordPress plugin
Added one custom field at checkout page, called - Address Type which is drop down contains 2 values. Business/Commercial and Residential
I have total 3 shipping methods - International Flat Rate, Flat Rate, Free Shipping
Now, condition that I want when end user select residential then and then they will get all 3 shipping methods but once user select a business option then hide/remove International Flat Rate and Flat Rate shipping methods (I mean show only Free Shipping).
add_filter( 'woocommerce_checkout_fields' , 'add_address_type_field' );
function add_address_type_field( $fields ) {
$address_type_field = array(
'type' => 'select',
'label' => __('Address Type', 'woocommerce'),
'required' => false,
'class' => array('address-field', 'form-row-wide', 'validate-addresstype'),
'clear' => true,
'options' => array(
'residential' => 'Residential',
'business' => 'Business/Commercial'
),
);
$fields['billing']['billing_address_type'] = $address_type_field;
$fields['shipping']['shipping_address_type'] = $address_type_field;
return $fields;
}
add_filter('woocommerce_package_rates', 'display_shipping_method_based_on_state', 10, 2);
if(!function_exists('display_shipping_method_based_on_state')) {
function display_shipping_method_based_on_state($rates) {
global $woocommerce;
$state = WC()->customer->shipping_state;
$address_type = WC()->customer->shipping_address_type;
$free_shipping = $rates['free_shipping'];
if($address_type == 'business')
{
$rates = array();
$rates['free_shipping'] = $free_shipping;
}
return $rates;
}
}
This is my code that I am trying. Let me know where I am wrong?
Thanks in advance!

Resources