Woocommerce multi Vendor Split Shipping without plugin or custom plugin - php

I Want To Calculate Shipping Rates and Display WooCommerce Shipping Methods on the Cart & Checkout Page (Either Calculate & Display WooCommerce Shipping Rates Separately for Each Vendor, Or, Add WooCommerce Shipping Cost for Each Vendor, and Display Final Shipping Cost on Cart Page).
There are many plugins available but I want to do it without a plugin(functions.php) or create my own plugin.
$packages[] = array(
'ship_via' => $shipping_method_ids ?: array('flat_rate', 'local_pickup', 'free_shipping'),
'name' => $shipping_class_name,
'contents' => $new_package,
'contents_cost' => array_sum(wp_list_pluck($new_package, 'line_total')),
'applied_coupons' => WC()->cart->applied_coupons,
'destination' => array(
'country' => WC()->customer->get_shipping_country(),
'state' => WC()->customer->get_shipping_state(),
'postcode' => WC()->customer->get_shipping_postcode(),
'city' => WC()->customer->get_shipping_city(),
'address' => WC()->customer->get_shipping_address(),
'address_2' => WC()->customer->get_shipping_address_2()
)
);
I have done create Separate package shipping wise. but I want to create the same (slipt shipping) according to Vendor Wise.
In short
Vendor A -> Shipping(1,2,3..)
Vendor B -> Shipping(1,2,3..)
Vendor C -> Shipping(1,2,3..)
.
.
.
etc...
See screenshoot for more Info.

Finally, we got an answer to this question.
First of all, we should get vendors associated with cart products.
// Get Vendor ID from Prduct ID - Method 1 (Compatible with woocommerce-product-vendors plugin)
function get_product_vendors_1( $product_id = null ) {
if ( null === $product_id ) {
return null;
}
$term = wp_get_object_terms( $product_id, WC_PRODUCT_VENDORS_TAXONOMY, array( 'fields' => 'ids' ) );
if ( is_wp_error( $term ) || empty( $term ) ) {
return null;
}
return $term[0];
}
// Get Vendor ID from Prduct ID - Method 2
function get_user_roles_by_user_id( $user_id = null, $role = array('vendors') ) {
$user = get_userdata( $user_id );
$user_roles = array_intersect($user->roles,$role);
if (isset($user_roles) && !empty($user_roles)) {
return $user_id;
}
return false;
}
function get_product_vendors_2($product_id = null){
// Vendors roles - you can change accordingly
$role = array(
'wc_product_vendors_pending_vendor',
'wc_product_vendors_manager_vendor',
'wc_product_vendors_admin_vendor'
);
$user_id = get_post_field( 'post_author', $product_id );
if(get_user_roles_by_user_id( $user_id, $role)){
return $user_id;
}
return false;
}
// Now you can use one of them
get_product_vendors_1($product_id)
or
get_product_vendors_2($product_id)
Now Multi-Vendor Split Shipping full Source code.
add_filter('woocommerce_cart_shipping_packages', 'woo_multi_vendors_shipping_packages');
function woo_multi_vendors_shipping_packages($packages)
{
$i = 1;
$packages = $vendor_items_map = array();
foreach (WC()->cart->get_cart() as $cart_item)
{
if ($cart_item['data']->needs_shipping())
{
$product_id = $cart_item['product_id'];
// Get vendors for each product.
// $vendor = get_product_vendors_2( $product_id);
$vendor = get_product_vendors_1( $product_id);
$vendor_id = isset($vendor) ? (int)$vendor : 0;
$vendor_items_map[$vendor_id][] = $cart_item;
}
}
foreach($vendor_items_map as $vendor_id => $vendor_items) {
if(!empty($vendor_items)){
$packages_name = null;
if($vendor_id){
$user_displayname = get_term($vendor_id)->name ?: '';
if($user_displayname){
$packages_name = '<span class="ship-name">Shipping #'.$i.'</span><span class="vendor-name">'.$user_displayname.'</span>';
}
}
$packages[] = array(
//'ship_via' => array( 'flat_rate' ),
'name' => $packages_name ?: null,
'contents' => $vendor_items,
'contents_cost' => array_sum( wp_list_pluck( $vendor_items, 'line_total' ) ),
'applied_coupons' => WC()->cart->applied_coupons,
'destination' => array(
'country' => WC()->customer->get_shipping_country(),
'state' => WC()->customer->get_shipping_state(),
'postcode' => WC()->customer->get_shipping_postcode(),
'city' => WC()->customer->get_shipping_city(),
'address' => WC()->customer->get_shipping_address(),
'address_2' => WC()->customer->get_shipping_address_2()
)
);
}
$i++;
}
return $packages;
}

Related

Generate woocommerce coupon after gravity form submission

I want a user to enter their email in a gravity form and after submission they are emailed a unique coupon code that expires after two weeks. I have cobble together code from a few other solutions and I am successful at creating the unique code. But I can't get it to create the coupon in woocommerce.. Not being a PHP master I know I'm missing something obvious.
//* Define options/constants
define( 'ENGWP_FORM_ID', 276 ); // The ID of the form (integer)
define( 'ENGWP_SOURCE_FIELD_ID', 2 ); // The ID of the form field holding the code (integer)
define( 'ENGWP_CODE_LENGTH', 12 ); // Length of code (integer)
define( 'ENGWP_CODE_CHARS', '1234567890QWERTYUIOPASDFGHJKLZXCVBNM' ); // Available character for the code; default 0-9 and uppercase letters (string)
define( 'ENGWP_CODE_PREFIX', '17-' ); // Custom prefix for the code (string); default empty
define( 'ENGWP_DISCOUNT_TYPE', 'percent' ); // 'flat' or 'percent' (string)
define( 'ENGWP_DISCOUNT_AMOUNT', 10 ); // Value of discount (integer); $ if 'type' is 'flat' and % if 'type' is 'percent'
define( 'ENGWP_MAX_USES', 1 ); // Maximum number of uses per customer (integer)
define( 'ENGWP_MIN_PRICE', 0 ); // Minimum price for discount to apply (integer); default none
define( 'ENGWP_PRODUCT_REQS', '' ); // A comma-separated list of product IDs (string) the coupons apply to
define( 'ENGWP_REQS_CONDITION', '' ); // How to apply the discount to those products (string); accepts 'any' (at least one product ID must be in the cart) or 'all' (all products must be in the cart)
define( 'ENGWP_SINGLE_USE', 'use_once' ); // Whether the coupons generated can be used more than once by a single customer; default is set to one-time usage but can be set to false (boolean) for allowing multiple uses
define( 'ENGWP_EXCLUDE_PRODUCTS', '' ); // A comma-separated list of product IDs (string) to exclude from discount-applicability
$start_date = ''; # no date
// $start_date = '01/01/1900'; # static date
// $start_date = date( 'm/d/Y', strtotime("yesterday") );
$exp_date = date( 'm/d/Y', strtotime("+14 days") );
// $exp_date = '01/01/1900'; # static date
// $exp_date = ''; # no date
class GW_Create_Coupon {
public function __construct( $args = array() ) {
// set our default arguments, parse against the provided arguments, and store for use throughout the class
$this->_args = wp_parse_args( $args, array(
'form_id' => false,
'source_field_id' => false,
'plugin' => 'wc',
'amount' => 0,
'type' => '',
'meta' => array()
) );
// do version check in the init to make sure if GF is going to be loaded, it is already loaded
add_action( 'init', array( $this, 'init' ) );
}
public function init() {
// make sure we're running the required minimum version of Gravity Forms
if( ! property_exists( 'GFCommon', 'version' ) || ! version_compare( GFCommon::$version, '1.8', '>=' ) ) {
return;
}
add_action( 'gform_after_submission', array( $this, 'create_coupon' ), 10, 2 );
}
public function create_coupon( $entry, $form ) {
if( ! $this->is_applicable_form( $form ) ) {
return;
}
$coupon_code = rgar( $entry, $this->_args['source_field_id'] );
$amount = $this->_args['amount'];
$type = $this->_args['type'];
$plugin_func = array( $this, sprintf( 'create_coupon_%s', $this->_args['plugin'] ) );
if( is_callable( $plugin_func ) ) {
call_user_func( $plugin_func, $coupon_code, $amount, $type );
}
}
public function create_coupon_wc( $coupon_code, $amount, $type ) {
$coupon = array(
‘post_title’ => $coupon_code,
‘post_content’ => ”,
‘post_status’ => ‘publish’,
‘post_author’ => 1,
‘post_type’ => ‘shop_coupon’
);
$new_coupon_id = wp_insert_post( $coupon );
$meta = wp_parse_args( $this->_args[‘meta’], array(
‘discount_type’ => $type,
‘coupon_amount’ => $amount,
‘individual_use’ => ‘yes’,
‘product_ids’ => ”,
‘exclude_product_ids’ => ”,
‘usage_limit’ => ‘1’,
‘expiry_date’ => ”,
‘apply_before_tax’ => ‘no’,
‘free_shipping’ => ‘no’,
‘exclude_sale_items’ => ‘no’,
‘product_categories’ => ”,
‘exclude_product_categories’ => ”,
‘minimum_amount’ => ”,
‘customer_email’ => ”
) );
foreach( $meta as $meta_key => $meta_value ) {
update_post_meta( $new_coupon_id, $meta_key, $meta_value );
}
}
public function create_coupon_edd( $coupon_code, $amount, $type ) {
if( ! is_callable( 'edd_store_discount' ) ) {
return;
}
$meta = wp_parse_args( $this->_args['meta'], array(
'name' => $coupon_code,
'code' => $coupon_code,
'type' => $type,
'amount' => $amount,
'excluded_products' => array(),
'expiration' => '',
'is_not_global' => false,
'is_single_use' => false,
'max_uses' => '',
'min_price' => '',
'product_condition' => '',
'product_reqs' => array(),
'start' => '',
'uses' => '',
) );
// EDD will set it's own defaults in the edd_store_discount() so let's filter out our own empty defaults (their just here for easier reference)
$meta = array_filter( $meta );
// EDD takes a $details array which has some different keys than the meta, let's map the keys to the expected format
$edd_post_keys = array(
'max_uses' => 'max',
'product_reqs' => 'products',
'excluded_products' => 'excluded-products',
'is_not_global' => 'not_global',
'is_single_use' => 'use_once'
);
foreach( $meta as $key => $value ) {
$mod_key = rgar( $edd_post_keys, $key );
if( $mod_key ) {
$meta[$mod_key] = $value;
}
}
edd_store_discount( $meta );
}
function is_applicable_form( $form ) {
$form_id = isset( $form['id'] ) ? $form['id'] : $form;
return $form_id == $this->_args['form_id'];
}
}
//* Instantiate the class for EDD
new GW_Create_Coupon( array(
'form_id' => ENGWP_FORM_ID,
'source_field_id' => ENGWP_SOURCE_FIELD_ID,
'amount' => ENGWP_DISCOUNT_AMOUNT,
'type' => ENGWP_DISCOUNT_TYPE,
'meta' => array(
'excluded_products' => array( ENGWP_EXCLUDE_PRODUCTS ),
'expiration' => $exp_date,
'is_not_global' => 'not_global',
'is_single_use' => ENGWP_SINGLE_USE,
'max_uses' => ENGWP_MAX_USES,
'min_price' => ENGWP_MIN_PRICE,
'product_condition' => ENGWP_REQS_CONDITION,
'product_reqs' => array( ENGWP_PRODUCT_REQS ),
'start' => $start_date,
)
) );
/**
* Generate the random codes to be used for the EDD discounts
*/
//* Generates and returns the code
add_filter( 'gform_field_value_uuid', 'gw_generate_unique_code' );
function gw_generate_unique_code() {
$length = ENGWP_CODE_LENGTH;
$chars = ENGWP_CODE_CHARS;
$prefix = ENGWP_CODE_PREFIX;
$unique = '';
$chars_length = strlen( $chars )-1;
for( $i = 0 ; $i < $length ; $i++ ) {
$unique .= $chars[ rand( 0, $chars_length ) ];
}
do {
$unique = $prefix . str_shuffle( $unique );
} while ( !gw_check_unique_code( $unique ) );
return $unique;
}
//* Checks to make sure the code generated is unique (not already in use)
function gw_check_unique_code( $unique ) {
global $wpdb;
$table = $wpdb->prefix . 'rg_lead_detail';
$form_id = ENGWP_FORM_ID; // update to the form ID your unique id field belongs to
$field_id = ENGWP_SOURCE_FIELD_ID; // update to the field ID your unique id is being prepopulated in
$result = $wpdb->get_var( "SELECT value FROM $table WHERE form_id = '$form_id' AND field_number = '$field_id' AND value = '$unique'" );
if ( empty ( $result ) ) {
return true;
} else return false;
}
You can use WC_Coupon to generate a coupon code. try the below code.
add_action( 'gform_after_submission', array( $this, 'create_coupon' ), 10, 2 );
public function create_coupon( $entry, $form ){
$coupon_code = rgar( $entry, $this->_args['source_field_id'] );
$date_expires = date('Y-m-d', strtotime('+14 days'));
$discount_type = 'fixed_cart'; // 'store_credit' doesn't exist
$coupon = new WC_Coupon();
$coupon->set_code($coupon_code);
//the coupon discount type can be 'fixed_cart', 'percent' or 'fixed_product', defaults to 'fixed_cart'
$coupon->set_discount_type($discount_type);
//the discount amount, defaults to zero
$coupon->set_amount($amount );
$coupon->set_date_expires( $date_expires );
//save the coupon
$coupon->save();
}

Create a shipping method for free items on woocommerce

I'm looking to create two separate shipping sets based on items that are in the cart.
I want free shipping on shipping class "holiday packages" and free shipping on product category "coffee" when there are more than 2 bags of coffee in the cart. Then I want the rest of the items to be put into a different shipping "container".
I currently can get it all to work when there are two different coffees in there (thus is up ticking the coffee count) but when there are two of one type of coffee it doesn't separate it.
/* Puts items with "holiday-package" shipping class into a different shipping package. */
function hdm_woocommerce_cart_shipping_packages( $packages ) {
// Reset the packages
$packages = array();
// Free items
$freeshipping_items = array();
$regular_items = array();
$coffee_count = 0;
//get product category and count up each item in that category
foreach ( WC()->cart->get_cart() as $item ) {
$quantity = $item['quantity'];
if ($item['data']->get_shipping_class() == 'coffee') {
$coffee_count++; }
elseif ($quantity >= 2 && $item['data']->get_shipping_class() == 'coffee') {
$coffee_count = 2;
}
}
// Sort free from regular.
foreach( WC()->cart->get_cart() as $item ) {
if( $item['data']->needs_shipping() ) {
if( $item['data']->get_shipping_class() == 'holiday-packages' ) {
$freeshipping_items[] = $item;
}
elseif ($item['data']->get_shipping_class() == 'coffee' && $coffee_count >= 2){
$freeshipping_items[] = $item;
}
else {
$regular_items[] = $item;
}
}
}
// Put inside packages:
if( $regular_items ) {
$packages[] = array(
'ship_via' => array(),
'contents' => $regular_items,
'contents_cost' => array_sum(wp_list_pluck($regular_items, 'line_total')),
'applied_coupons' => WC()->cart->applied_coupons,
'destination' => array(
'country' => WC()->customer->get_shipping_country(),
'state' => WC()->customer->get_shipping_state(),
'postcode' => WC()->customer->get_shipping_postcode(),
'city' => WC()->customer->get_shipping_city(),
'address' => WC()->customer->get_shipping_address(),
'address_2' => WC()->customer->get_shipping_address_2()
)
);
}
if( $freeshipping_items ) {
$packages[] = array(
'ship_via' => array( 'flat_rate' ),
'contents' => $freeshipping_items,
'contents_cost' => array_sum(wp_list_pluck($freeshipping_items, 'line_total')),
'applied_coupons' => WC()->cart->applied_coupons,
'destination' => array(
'country' => WC()->customer->get_shipping_country(),
'state' => WC()->customer->get_shipping_state(),
'postcode' => WC()->customer->get_shipping_postcode(),
'city' => WC()->customer->get_shipping_city(),
'address' => WC()->customer->get_shipping_address(),
'address_2' => WC()->customer->get_shipping_address_2()
)
);
}
return $packages;
}
add_filter('woocommerce_cart_shipping_packages', 'hdm_woocommerce_cart_shipping_packages');
UPDATE: I was about to fix it by changing
elseif ($quantity >= 2 && $item['data']->get_shipping_class() == 'coffee') {
$coffee_count = 2;
}
to
if ($quantity >= 2 && $item['data']->get_shipping_class() == 'coffee') {
$coffee_count = 2;
}
I don't think it will come up with the correct quantities, but it works for my needs. If anyone has any advice on how to better write this it would be much appreciated.

Setting the product type as a variable subscription in WooCommerce

I'm learning Woocommerce and I've managed to create a product programmatically. Currently, the product is being set as a variable product image
However, this needs to be a variable subscription. How should I do so? Tried looking in the wc_product class etc... Even though that replacing:
$product = new WC_Product_Variable( $product_id );
with
$product = new WC_Subscription_Variable( $product_id );
would work... It didn't obviously... So I'll try looking for an answer, but maybe if you knew what to do, then that would be amazing :)
My code:
create_product_variation( array(
'author' => '', // optional
'title' => $oVehicle['koeretoej']['titel'],
'excerpt' => 'The product short description…',
'regular_price' => '0', // product regular price
'sale_price' => '', // product sale price (optional)
'stock' => '1', // Set a minimal stock quantity
'image_id' => '', // optional
'gallery_ids' => array(), // optional
'sku' => '', // optional
'tax_class' => '', // optional
'weight' => '', // optional
// For NEW attributes/values use NAMES (not slugs)
'attributes' => array(
'Perioder' => array( '1 Uge', '2 Uger', '3 Uger', '4 Uger', '2 Måneder',),
// 'Attribute 2' => array( 'Value 1', 'Value 2', 'Value 3' ),
),
) );
}
}
$response['existingvehicles'] = $existingvehicles;
$response['$iProductID'] = $product;
$response['vehicles'] = $vehicles;
$response['aVehicles'] = $_POST['aVehicles'];
$response['bSucces'] = true;
echo json_encode($response);
wp_die();
}
function save_product_attribute_from_name( $name, $label='', $set=true ){
if( ! function_exists ('get_attribute_id_from_name') ) return;
global $wpdb;
$label = $label == '' ? ucfirst($name) : $label;
$attribute_id = get_attribute_id_from_name( $name );
if( empty($attribute_id) ){
$attribute_id = NULL;
} else {
$set = false;
}
$args = array(
'attribute_id' => $attribute_id,
'attribute_name' => $name,
'attribute_label' => $label,
'attribute_type' => 'select',
'attribute_orderby' => 'menu_order',
'attribute_public' => 0,
);
if( empty($attribute_id) ) {
$wpdb->insert( "{$wpdb->prefix}woocommerce_attribute_taxonomies", $args );
set_transient( 'wc_attribute_taxonomies', false );
}
if( $set ){
$attributes = wc_get_attribute_taxonomies();
$args['attribute_id'] = get_attribute_id_from_name( $name );
$attributes[] = (object) $args;
//print_r($attributes);
set_transient( 'wc_attribute_taxonomies', $attributes );
} else {
return;
}
}
/**
* Get the product attribute ID from the name.
*
* #since 3.0.0
* #param string $name | The name (slug).
*/
function get_attribute_id_from_name( $name ){
global $wpdb;
$attribute_id = $wpdb->get_col("SELECT attribute_id
FROM {$wpdb->prefix}woocommerce_attribute_taxonomies
WHERE attribute_name LIKE '$name'");
return reset($attribute_id);
}
/**
* Create a new variable product (with new attributes if they are).
* (Needed functions:
*
* #since 3.0.0
* #param array $data | The data to insert in the product.
*/
function create_product_variation( $data ){
if( ! function_exists ('save_product_attribute_from_name') ) return;
$postname = sanitize_title( $data['title'] );
$author = empty( $data['author'] ) ? '1' : $data['author'];
$post_data = array(
'post_author' => $author,
'post_name' => $postname,
'post_title' => $data['title'],
'post_content' => $data['content'],
'post_excerpt' => $data['excerpt'],
'post_status' => 'publish',
'ping_status' => 'closed',
'post_type' => 'product',
'guid' => home_url( '/product/'.$postname.'/' ),
);
// Creating the product (post data)
$product_id = wp_insert_post( $post_data );
// Get an instance of the WC_Product_Variable object and save it
$product = new WC_Product_Variable( $product_id );
$product->save();
## ---------------------- Other optional data ---------------------- ##
## (see WC_Product and WC_Product_Variable setters methods)
// THE PRICES (No prices yet as we need to create product variations)
// IMAGES GALLERY
if( ! empty( $data['gallery_ids'] ) && count( $data['gallery_ids'] ) > 0 )
$product->set_gallery_image_ids( $data['gallery_ids'] );
// SKU
if( ! empty( $data['sku'] ) )
$product->set_sku( $data['sku'] );
// STOCK (stock will be managed in variations)
$product->set_stock_quantity( $data['stock'] ); // Set a minimal stock quantity
$product->set_manage_stock(true);
$product->set_stock_status('');
// Tax class
if( empty( $data['tax_class'] ) )
$product->set_tax_class( $data['tax_class'] );
// WEIGHT
if( ! empty($data['weight']) )
$product->set_weight(''); // weight (reseting)
else
$product->set_weight($data['weight']);
$product->validate_props(); // Check validation
## ---------------------- VARIATION ATTRIBUTES ---------------------- ##
$product_attributes = array();
foreach( $data['attributes'] as $key => $terms ){
$taxonomy = wc_attribute_taxonomy_name($key); // The taxonomy slug
$attr_label = ucfirst($key); // attribute label name
$attr_name = ( wc_sanitize_taxonomy_name($key)); // attribute slug
// NEW Attributes: Register and save them
if( ! taxonomy_exists( $taxonomy ) )
save_product_attribute_from_name( $attr_name, $attr_label );
$product_attributes[$taxonomy] = array (
'name' => $taxonomy,
'value' => '',
'position' => '',
'is_visible' => 1,
'is_variation' => 1,
'is_taxonomy' => 1
);
foreach( $terms as $value ){
$term_name = ucfirst($value);
$term_slug = sanitize_title($value);
// Check if the Term name exist and if not we create it.
if( ! term_exists( $value, $taxonomy ) )
wp_insert_term( $term_name, $taxonomy, array('slug' => $term_slug ) ); // Create the term
// Set attribute values
wp_set_post_terms( $product_id, $term_name, $taxonomy, true );
}
}
update_post_meta( $product_id, '_product_attributes', $product_attributes );
// $product->set_attributes(array( $taxonomy => $terms[0] ));
$product->save(); // Save the data
}
Replacing this:
$product = new WC_Product_Variable( $product_id );
with this
$product = new WC_Product_Variable_Subscription( $product_id );
did the job

Display Products Shipping Classes in WooCommerce order edit page

I'm using a function that allows me to calculate more than one freight in the cart based on the shipping class of each product. The function has no problem, it's perfect. But when I check the request in the area of administration of woocommerce, I need to identify in the method of submission, the name of the class corresponding to each item.
That is, I need to differentiate the items for each class, because I am using the shipping class to inform different suppliers about the orders made at the store.
But I have no idea how to make this adjustment.
Below is the function I use, working with packages based on the created classes. This function is responsible for calculating the freight in the shopping cart according to the shipping class. How do I display the shipping class name on the order page in the store admin?
function custom_split_shipping_packages_shipping_class( $packages ) {
// Reset all packages
$packages = array();
$regular_package_items = array();
$split_package_items = array();
$split_shipping_class = 'da-vinci'; // Shipping class slug
foreach ( WC()->cart->get_cart() as $item_key => $item ) {
if ( $item['data']->needs_shipping() ) {
if ( $split_shipping_class == $item['data']->get_shipping_class() ) {
$split_package_items[ $item_key ] = $item;
} else {
$regular_package_items[ $item_key ] = $item;
}
}
}
// Create shipping packages
if ( $regular_package_items ) {
$packages[] = array(
'contents' => $regular_package_items,
'contents_cost' => array_sum( wp_list_pluck( $regular_package_items, 'line_total' ) ),
'applied_coupons' => WC()->cart->get_applied_coupons(),
'user' => array(
'ID' => get_current_user_id(),
),
'destination' => array(
'country' => WC()->customer->get_shipping_country(),
'state' => WC()->customer->get_shipping_state(),
'postcode' => WC()->customer->get_shipping_postcode(),
'city' => WC()->customer->get_shipping_city(),
'address' => WC()->customer->get_shipping_address(),
'address_2' => WC()->customer->get_shipping_address_2()
)
);
}
if ( $split_package_items ) {
$packages[] = array(
'contents' => $split_package_items,
'contents_cost' => array_sum( wp_list_pluck( $split_package_items, 'line_total' ) ),
'applied_coupons' => WC()->cart->get_applied_coupons(),
'user' => array(
'ID' => get_current_user_id(),
),
'destination' => array(
'country' => WC()->customer->get_shipping_country(),
'state' => WC()->customer->get_shipping_state(),
'postcode' => WC()->customer->get_shipping_postcode(),
'city' => WC()->customer->get_shipping_city(),
'address' => WC()->customer->get_shipping_address(),
'address_2' => WC()->customer->get_shipping_address_2()
)
);
}
return $packages;
}
add_filter( 'woocommerce_cart_shipping_packages', 'custom_split_shipping_packages_shipping_class' );
I can't get yet the way to to display the shipping classes in order shipping items. but I can display this shipping classes in Order edit "line_items" this way:
// Display the shipping classes names in Order edit line items
add_action( 'woocommerce_after_order_itemmeta', 'custom_admin_order_itemmeta', 15, 3 );
function custom_admin_order_itemmeta( $item_id, $item, $product ){
if( ! is_admin() ) return; // only backend
// Display the shipping class names if they exist
if( $item->is_type( 'line_item' ) ){
$label = __( 'Shipping class:', 'woocommerce' );
$class_id = $product->get_shipping_class_id();
if( $class_id > 0 ){
$term = get_term_by( 'id', $class_id, 'product_shipping_class' );
$class_name = esc_html($term->name);
}
if( isset($class_name) )
echo '<div class="wc-order-item-ship-class"><strong>' . $label . '</strong> <span style="color:#ca4a1f;">' . $class_name . '</span></div>';
}
}
Code goes in function.php file of your active child theme (or active theme) or in any plugin file.
Tested and works. You will get something like:
Note: On Woocommerce 3.2.x I get an error when checking out, related to your actual code.

Create an order programmatically with line items in Woocommerce 3+

I needed to create a Woocommerce order programatically, however using the 'old' Woocommerce made this a very dirty procedure.
I had to insert all kind of database records manually, using many update_post_meta calls.
Looking for a better solution.
With latest version of WooCommerce is possible try this as something like
$address = array(
'first_name' => 'Fresher',
'last_name' => 'StAcK OvErFloW',
'company' => 'stackoverflow',
'email' => 'test#test.com',
'phone' => '777-777-777-777',
'address_1' => '31 Main Street',
'address_2' => '',
'city' => 'Chennai',
'state' => 'TN',
'postcode' => '12345',
'country' => 'IN'
);
$order = wc_create_order();
$order->add_product( get_product( '12' ), 2 ); //(get_product with id and next is for quantity)
$order->set_address( $address, 'billing' );
$order->set_address( $address, 'shipping' );
$order->add_coupon('Fresher','10','2'); // accepted param $couponcode, $couponamount,$coupon_tax
$order->calculate_totals();
Call this above code with your function then it will work accordingly.
Note it not work with old version of WooCommerce like 2.1.12, It works only from 2.2 of WooCommerce.
Hope it helps
2017-2021 For WooCommerce 3 and Above
Woocommerce 3 has introduced CRUD objects and there is lot of changes on Order items… Also some WC_Order methods are now deprecated like add_coupon().
Here is a function that allow creating programmatically an order nicely with all required data in it, including the taxes:
function create_wc_order( $data ){
$gateways = WC()->payment_gateways->get_available_payment_gateways();
$order = new WC_Order();
// Set Billing and Shipping adresses
foreach( array('billing_', 'shipping_') as $type ) {
foreach ( $data['address'] as $key => $value ) {
if( $type === 'shipping_' && in_array( $key, array( 'email', 'phone' ) ) )
continue;
$type_key = $type.$key;
if ( is_callable( array( $order, "set_{$type_key}" ) ) ) {
$order->{"set_{$type_key}"}( $value );
}
}
}
// Set other details
$order->set_created_via( 'programatically' );
$order->set_customer_id( $data['user_id'] );
$order->set_currency( get_woocommerce_currency() );
$order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
$order->set_customer_note( isset( $data['order_comments'] ) ? $data['order_comments'] : '' );
$order->set_payment_method( isset( $gateways[ $data['payment_method'] ] ) ? $gateways[ $data['payment_method'] ] : $data['payment_method'] );
$calculate_taxes_for = array(
'country' => $data['address']['country'],
'state' => $data['address']['state'],
'postcode' => $data['address']['postcode'],
'city' => $data['address']['city']
);
// Line items
foreach( $data['line_items'] as $line_item ) {
$args = $line_item['args'];
$product = wc_get_product( isset($args['variation_id']) && $args['variation_id'] > 0 ? $$args['variation_id'] : $args['product_id'] );
$item_id = $order->add_product( $product, $line_item['quantity'], $line_item['args'] );
$item = $order->get_item( $item_id, false );
$item->calculate_taxes($calculate_taxes_for);
$item->save();
}
// Coupon items
if( isset($data['coupon_items'])){
foreach( $data['coupon_items'] as $coupon_item ) {
$order->apply_coupon(sanitize_title($coupon_item['code']));
}
}
// Fee items
if( isset($data['fee_items'])){
foreach( $data['fee_items'] as $fee_item ) {
$item = new WC_Order_Item_Fee();
$item->set_name( $fee_item['name'] );
$item->set_total( $fee_item['total'] );
$tax_class = isset($fee_item['tax_class']) && $fee_item['tax_class'] != 0 ? $fee_item['tax_class'] : 0;
$item->set_tax_class( $tax_class ); // O if not taxable
$item->calculate_taxes($calculate_taxes_for);
$item->save();
$order->add_item( $item );
}
}
// Set calculated totals
$order->calculate_totals();
if( isset($data['order_status']) ) {
// Update order status from pending to your defined status and save data
$order->update_status($data['order_status']['status'], $data['order_status']['note']);
$order_id = $order->get_id();
} else {
// Save order to database (returns the order ID)
$order_id = $order->save();
}
// Returns the order ID
return $order_id;
}
Code goes in function.php file of your active child theme (or active theme) or in a plugin file.
USAGE EXAMPLE from a data array:
create_wc_order( array(
'address' => array(
'first_name' => 'Fresher',
'last_name' => 'StAcK OvErFloW',
'company' => 'stackoverflow',
'email' => 'test1#testoo.com',
'phone' => '777-777-777-777',
'address_1' => '31 Main Street',
'address_2' => '',
'city' => 'Chennai',
'state' => 'TN',
'postcode' => '12345',
'country' => 'IN',
),
'user_id' => '',
'order_comments' => '',
'payment_method' => 'bacs',
'order_status' => array(
'status' => 'on-hold',
'note' => '',
),
'line_items' => array(
array(
'quantity' => 1,
'args' => array(
'product_id' => 37,
'variation_id' => '',
'variation' => array(),
)
),
),
'coupon_items' => array(
array(
'code' => 'summer',
),
),
'fee_items' => array(
array(
'name' => 'Delivery',
'total' => 5,
'tax_class' => 0, // Not taxable
),
),
) );
With the new release of WC 2, it's much better.
However:
I do not want to use the REST API, cause I am doing a call from my own WP plugin directly. I see no use in doing a curl to my localhost
The 'WooCommerce REST API Client Library' is not useful for me cause it relay's on the REST API and it doesn't support a Create Order call
To be honest, WooCom's API Docs are limited, maybe they are still in the progress of updating it. They currently don't tell me how to create a new order, which params are required etc.
Any way, I figured out how to create an order with a line order (your product) using the classes and functions used by the REST API and I want to share it!
I created my own PHP class:
class WP_MyPlugin_woocommerce
{
public static function init()
{
// required classes to create an order
require_once WOOCOMMERCE_API_DIR . 'class-wc-api-exception.php';
require_once WOOCOMMERCE_API_DIR . 'class-wc-api-server.php';
require_once WOOCOMMERCE_API_DIR . 'class-wc-api-resource.php';
require_once WOOCOMMERCE_API_DIR . 'interface-wc-api-handler.php';
require_once WOOCOMMERCE_API_DIR . 'class-wc-api-json-handler.php';
require_once WOOCOMMERCE_API_DIR . 'class-wc-api-orders.php';
}
public static function create_order()
{
global $wp;
// create order
$server = new WC_API_Server( $wp->query_vars['wc-api-route'] );
$order = new WC_API_Orders( $server );
$order_id = $order->create_order( array
(
'order' => array
(
'status' => 'processing'
, 'customer_id' => get_current_user_id()
// , 'order_meta' => array
// (
// 'some order meta' => 'a value
// , some more order meta' => 1
// )
, 'shipping_address' => array
(
'first_name' => $firstname
, 'last_name' => $lastname
, 'address_1' => $address
, 'address_2' => $address2
, 'city' => $city
, 'postcode' => $postcode
, 'state' => $state
, 'country' => $country
)
, 'billing_address' => array(..can be same as shipping )
, 'line_items' => array
(
array
(
'product_id' => 258
, 'quantity' => 1
)
)
)
) );
var_dump($order_id);
die();
}
}
Important:
The 'WOOCOMMERCE_API_DIR' constant points to '/woocommerce/includes/api/' in your plugin dir.
The order is assigned to a customer, in my case the current logged in user. Make sure your user has a role that has the capability to read, edit, create and delete orders. My role looks like this:
$result = add_role(
'customer'
, __( 'Customer' )
, array
(
'read' => true
// , 'read_private_posts' => true
// , 'read_private_products' => true
, 'read_private_shop_orders' => true
, 'edit_private_shop_orders' => true
, 'delete_private_shop_orders' => true
, 'publish_shop_orders' => true
// , 'read_private_shop_coupons' => true
, 'edit_posts' => false
, 'delete_posts' => false
, 'show_admin_bar_front' => false
)
);
If you want to look at the shop manager's rights, check
var_dump(get_option( 'wp_user_roles'));
My create_order function nicely creates an order, with the lineitem in the order_items tables.
Hope I helped you out, it took me a while to get it right.
A lot of answers do you show the best filter(hook) to use. I searched for days because when I used the 'init' it kept making more orders. In 2 mins I had 30 orders. Anyway use this code and 1 order will be created. Also, change $product = wc_get_product( '1001' ) to your product id.Reference is here on line 144: https://github.com/dipolukarov/wordpress/blob/master/wp-content/plugins/woocommerce/classes/class-wc-checkout.php
function my_init2() {
$order = wc_create_order();
$order_id = $order->get_id();
$product = wc_get_product( '10001' );
$address = array(
'first_name' => 'John2',
'last_name' => 'Smith1',
'email' => 'johnsmith1#gmail.com',
);
//$order->date_created(2020-07-21 );
$order->add_product( $product, 1 );
$order->set_address( $address, 'billing' );
$order->set_created_via( 'programatically' );
$order->calculate_totals();
$order->set_total( $product->get_price() );
$order->update_status( 'wc-completed' );
error_log( '$order_id: ' . $order_id );
}
//add_action('admin_init','my_init2');
function my_run_only_once() {
if ( get_option( 'my_run_only_once_20' ) != 'completed' ) {
my_init2();
update_option( 'my_run_only_once_20', 'completed' );
}
}
add_action( 'admin_init', 'my_run_only_once' );

Categories