I am using this function to create a new order for my customers...
public function create_order( $user_id, $address, $product_id ) {
if ( ! $address ) return false;
$address = array(
'first_name' => $address->billing_first_name,
'last_name' => $address->billing_last_name,
'email' => $address->billing_email,
'phone' => $address->billing_phone,
'address_1' => $address->billing_address_1,
'city' => $address->billing_city,
'state' => $address->billing_state,
'postcode' => $address->billing_postcode,
'country' => $address->billing_country
);
$default_args = array(
'status' => 'wc-pending',
'customer_id' => $user_id,
);
// Now we create the order
$order = wc_create_order( $default_args );
$order->add_product( get_product( $product_id ), 1 ); // This is an existing SIMPLE product
// set default address to the created order
$order->set_address( $address, 'billing' );
//calculate total order price
$order->calculate_totals();
$order->update_status( "pending", 'Order Created Successfully And Payment is not completed', TRUE );
return $order;
}
This does create a new order but it doesn't calculate shipping cost... See below screenshot
Created Order Total Table
I have flat rate shipping cost based on User Billing State and Total Item QTY.... so how do i set shipping calculation automatically like it happens on checkout..
Related
I have the following code to split an order and create a new order with a custom status.
What I require is to notify the administrator when that new order is generated. So far the notification codes for a custom order have not worked.
The code to divide it that I am using is this:
function action_woocommerce_checkout_order_processed( $order_id, $posted_data, $order ) {
// Initialize
$check_for_back_orders = false;
// Loop through order items
foreach ( $order->get_items() as $item_key => $item ) {
// Get product
$product = $item->get_product();
// Product is on backorder
if ( $product->is_on_backorder() ) {
// Will only be executed once if the order contains back orders
if ( $check_for_back_orders == false ) {
$check_for_back_orders = true;
// Create new order with backorders
$backorder_order = wc_create_order();
}
// Add product to 'backorder' order
$backorder_order->add_product( $product, $item['quantity'] );
// Delete item from original order
$order->remove_item( $item->get_id() );
}
}
// If current order contains backorders, retrieve the necessary data from the existing order and apply it in the new order
if ( $check_for_back_orders ) {
// Recalculate and save original order
$order->calculate_totals();
$order->save();
// Obtain necessary information
// Get address
$address = array(
'first_name' => $order->get_billing_first_name(),
'last_name' => $order->get_billing_last_name(),
'email' => $order->get_billing_email(),
'phone' => $order->get_billing_phone(),
'address_1' => $order->get_billing_address_1(),
'address_2' => $order->get_billing_address_2(),
'city' => $order->get_billing_city(),
'state' => $order->get_billing_state(),
'postcode' => $order->get_billing_postcode(),
'country' => $order->get_billing_country()
);
// Get shipping
$shipping = array(
'first_name' => $order->get_shipping_first_name(),
'last_name' => $order->get_shipping_last_name(),
'address_1' => $order->get_shipping_address_1(),
'address_2' => $order->get_shipping_address_2(),
'city' => $order->get_shipping_city(),
'state' => $order->get_shipping_state(),
'postcode' => $order->get_shipping_postcode(),
'country' => $order->get_shipping_country()
);
// Get order currency
$currency = $order->get_currency();
// Get order payment method
$payment_gateway = $order->get_payment_method();
// Required information has been obtained, assign it to the 'backorder' order
// Set address
$backorder_order->set_address( $address, 'billing' );
$backorder_order->set_address( $shipping, 'shipping' );
// Set the correct currency and payment gateway
$backorder_order->set_currency( $currency );
$backorder_order->set_payment_method( $payment_gateway );
// Calculate totals
$backorder_order->calculate_totals();
// Set order note with original ID
$backorder_order->add_order_note( 'Pedido a confirmar stock automatizado. Creado a partir del ID de pedido original: ' . $order_id );
// Optional: give the new 'backorder' order the correct status
$backorder_order->update_status( 'wc-shipping-progress' );
// $backorder_order->update_status( 'on-hold' );
}
}
add_action( 'woocommerce_checkout_order_processed', 'action_woocommerce_checkout_order_processed', 10, 3 );
The code for the new custom status is this:
// Register New Order Statuses
function wpex_wc_register_post_statuses() {
register_post_status( 'wc-shipping-progress', array(
'label' => _x( 'Confirmar Stock', 'WooCommerce Order status', 'text_domain' ),
'public' => true,
'exclude_from_search' => false,
'show_in_admin_all_list' => true,
'show_in_admin_status_list' => true,
'label_count' => _n_noop( 'Approved (%s)', 'Approved (%s)', 'text_domain' )
) );
}
add_filter( 'init', 'wpex_wc_register_post_statuses' );
// Add New Order Statuses to WooCommerce
function wpex_wc_add_order_statuses( $order_statuses ) {
$order_statuses['wc-shipping-progress'] = _x( 'Confirmar Stock', 'WooCommerce Order status', 'text_domain' );
return $order_statuses;
}
add_filter( 'wc_order_statuses', 'wpex_wc_add_order_statuses' );
So far the notification codes for a custom order have not worked.
I want to create an order (programmatically) of a Subscription Product. I found code from this question: Programmatically creating new order in Woocommerce, which I used to try make it work.
I was not able to make it work neither with a "Simple Subscription" nor a "Simple Product".
function create_vip_order() {
global $woocommerce;
$product_id = 123;
$address = array(
'first_name' => '111Joe',
'last_name' => 'Conlin',
'company' => 'Speed Society',
'email' => 'joe#testing.com',
'phone' => '760-555-1212',
'address_1' => '123 Main st.',
'address_2' => '104',
'city' => 'San Diego',
'state' => 'Ca',
'postcode' => '92121',
'country' => 'US'
);
// Now we create the order
$order = wc_create_order();
// The add_product() function below is located in /plugins/woocommerce/includes/abstracts/abstract_wc_order.php
$order->add_product( get_product( $product_id ), 1 ); // This is an existing SIMPLE product
$order->set_address( $address, 'billing' );
//
$order->calculate_totals();
$order->update_status("Completed", 'Imported order', TRUE);
}
add_action( 'woocommerce_checkout_process', 'create_vip_order' );
If you have any solution I would really appreciate it. :)
Best Regards, Ledung
EDIT: It seem the way my Product were set up was causing the function not to work. I am currently using a Simple Subscription Product, with the "Virtual" parameter checked and a subscription length of 3 months.
Here you can see the current code that work with my setup:
function create_test_sub() {
$email = 'test#test.se';
$start_date = date( "Y-m-d H:i:s", strtotime( "now" ) );
$address = array(
'first_name' => 'Firstname',
'last_name' => 'Secondname',
'company' => 'Company',
'email' => $email,
'phone' => '',
'address_1' => 'Streetname 123',
'address_2' => '',
'city' => 'City',
'postcode' => '12345',
'country' => 'Country'
);
$default_password = wp_generate_password();
if ( ! $user = get_user_by( 'login', $email ) ) $user = wp_create_user( $email, $default_password, $email );
// I've used one product with multiple variations
$parent_product = wc_get_product( 3901 );
$args = array(
'attribute_billing-period' => 'Yearly',
'attribute_subscription-type' => 'Both'
);
$product_variation = $parent_product->get_matching_variation( $args );
$product = wc_get_product( $product_variation );
$product_month_length = $parent_product->subscription_length;
$end_date = date( "Y-m-d H:i:s", strtotime( $product_month_length . "months" ) );
$quantity = 1;
// As far as I can see, you need to create the order first, then the sub
$order = wc_create_order( array( 'customer_id' => $user->id ) );
$order->add_product( $parent_product, $quantity, $args );
$order->set_address( $address, 'billing' );
$order->set_address( $address, 'shipping' );
$order->calculate_totals();
$order->update_status( "completed", 'Imported order', TRUE );
// Order created, now create sub attached to it -- optional if you're not creating a subscription, obvs
// Each variation has a different subscription period
$period = WC_Subscriptions_Product::get_period( $parent_product );
$interval = WC_Subscriptions_Product::get_interval( $product );
$sub = wcs_create_subscription( array( 'status' => 'active', 'order_id' => $order->id, 'billing_period' => $period, 'billing_interval' => $interval, 'start_date' => $start_date, 'end' => $end_date ) );
if ( ! is_wp_error( $sub ) ){
$sub->add_product( $parent_product, $quantity, $args );
$sub->set_address( $address, 'billing' );
$sub->set_address( $address, 'shipping' );
$dates = array(
'end' => $end_date,
);
$sub->update_dates( $dates );
$sub->calculate_totals();
}
WC_Subscriptions_Manager::activate_subscriptions_for_order( $order );
print "<a href='/wp-admin/post.php?post=" . $sub->id . "&action=edit'>Sub created! Click here to edit</a>";
$returnarray = array(
'subscription_id' => $sub->id,
'order_id' => $order->id,
);
return $returnarray;
}
$test = create_test_sub();
Here's my custom function I built based on all the answers I found on SO and digging through subscriptions code base.
Tested with
WordPress 5.2.5
WooCommerce 3.8.0
WooCommerce Subscriptions 2.6.1
https://gist.github.com/tripflex/a3123052f36daf18f7cb05391d752223
function give_user_subscription( $product, $user_id, $note = '' ){
// First make sure all required functions and classes exist
if( ! function_exists( 'wc_create_order' ) || ! function_exists( 'wcs_create_subscription' ) || ! class_exists( 'WC_Subscriptions_Product' ) ){
return false;
}
$order = wc_create_order( array( 'customer_id' => $user_id ) );
if( is_wp_error( $order ) ){
return false;
}
$user = get_user_by( 'ID', $user_id );
$fname = $user->first_name;
$lname = $user->last_name;
$email = $user->user_email;
$address_1 = get_user_meta( $user_id, 'billing_address_1', true );
$address_2 = get_user_meta( $user_id, 'billing_address_2', true );
$city = get_user_meta( $user_id, 'billing_city', true );
$postcode = get_user_meta( $user_id, 'billing_postcode', true );
$country = get_user_meta( $user_id, 'billing_country', true );
$state = get_user_meta( $user_id, 'billing_state', true );
$address = array(
'first_name' => $fname,
'last_name' => $lname,
'email' => $email,
'address_1' => $address_1,
'address_2' => $address_2,
'city' => $city,
'state' => $state,
'postcode' => $postcode,
'country' => $country,
);
$order->set_address( $address, 'billing' );
$order->set_address( $address, 'shipping' );
$order->add_product( $product, 1 );
$sub = wcs_create_subscription(array(
'order_id' => $order->get_id(),
'status' => 'pending', // Status should be initially set to pending to match how normal checkout process goes
'billing_period' => WC_Subscriptions_Product::get_period( $product ),
'billing_interval' => WC_Subscriptions_Product::get_interval( $product )
));
if( is_wp_error( $sub ) ){
return false;
}
// Modeled after WC_Subscriptions_Cart::calculate_subscription_totals()
$start_date = gmdate( 'Y-m-d H:i:s' );
// Add product to subscription
$sub->add_product( $product, 1 );
$dates = array(
'trial_end' => WC_Subscriptions_Product::get_trial_expiration_date( $product, $start_date ),
'next_payment' => WC_Subscriptions_Product::get_first_renewal_payment_date( $product, $start_date ),
'end' => WC_Subscriptions_Product::get_expiration_date( $product, $start_date ),
);
$sub->update_dates( $dates );
$sub->calculate_totals();
// Update order status with custom note
$note = ! empty( $note ) ? $note : __( 'Programmatically added order and subscription.' );
$order->update_status( 'completed', $note, true );
// Also update subscription status to active from pending (and add note)
$sub->update_status( 'active', $note, true );
return $sub;
}
Need some help with WooCommerce adding variations to product attributes.
I created an attribute called Data ( pa_data ), this attribute stores terms from a custom field added with ACF, this field is a date picker.
I want to create an event that is recurring, and for every recurrence, I want to add a variation with price and stock.
So the process is like this:
Add all ACF fields in an array.
Create Variations with this array.
Add variations to attributes.
The problems is that i don't know how to add variations to the attribute ( pa_data );
Here is how is displaying my attributes
https://i.imgur.com/YmbDFlO.png
Here is how i want to be displayed
https://i.imgur.com/oDNvuOD.png
$days = array
array(
'date' => 'April 02, 2020',
'price' => '10',
),
array(
'date' => 'April 03, 2020',
'price' => '20',
),
array(
'date' => 'April 04, 2020',
'price' => '10',
),
);
// This method inserts new data into the post after the post is saved
add_action('post_updated', 'ProductUpdate', 10, 3);
// Callback function after the post is saved
function ProductUpdate($post_id, $post_after, $post_before) {
if ( $post_after && get_post_type($post_id) == 'product' ) {
ProductSetVariations( $post_id, $postDataVariations );
}
};
function ProductSetVariations($productID, $days ) {
// Get product object
$product = wc_get_product($productID);
foreach ($days as $day ) {
$date = $day['date'];
$price = $day['price'];
$variationPost = array(
'post_title' => $product->get_title(),
'post_name' => 'product-' . $productID . '-variation',
'post_status' => 'publish',
'post_parent' => $productID,
'post_type' => 'product_variation',
'guid' => $product->get_permalink()
);
// Insert the new variation post;
$variationID = wp_insert_post($variationPost);
// Create the new post based on the id
$variation = new WC_Product_Variation($variationID);
$taxonomy = 'pa_data';
// If term dosent exsist in taxonomy than add it
if ( !term_exists( $date, $taxonomy )) {
wp_insert_term( $date, $taxonomy );
};
$term_slug = get_term_by('name', $date, $taxonomy)->slug; // Get the term slug
$post_term_names = wp_get_post_terms( $productID, $taxonomy, array('fields' => 'names') );
if( ! in_array( $date, $post_term_names ) ) {
wp_set_post_terms( $productID, $date, $taxonomy, true );
};
$term_slug = get_term_by('name', $date, $taxonomy)->slug; // Get the term slug
update_post_meta( $variationID, 'attribute_'.$taxonomy, $term_slug );
$variation->set_price($price);
$variation->save(); // Save the data
}
};
Resolved!
Solution:
The problem was with WordPress hook 'post_updated', it does save in the database the changes but it doesn't change it in admin.
Here is the solution, for someone who has to update posts from rest api or just post update.
This is only a simple demonstration.
add_action('woocommerce_update_product', 'ProductUpdate', 10, 3);
function ProductUpdate($post_id) {
// Insert Product Attributes
function insert_product_attributes ($post_id, $variations) {
$product = wc_get_product($post_id);
// Set up an array to store the current attributes values.
$values = array();
foreach ($variations as $variation) {
array_push($values, $variation['date']);
}
wp_set_object_terms($post_id, $values, 'pa_data', true );
$product_attributes_data = array('pa_data' =>
array(
'name' => 'pa_data',
'position' => '1',
'is_visible' => '1',
'is_variation' => '1',
'is_taxonomy' => '1',
);
);
update_post_meta($post_id, '_product_attributes', $product_attributes_data);
};
function insert_product_variations ($post_id, $variations) {
foreach ($variations as $index => $variation) {
$attribute_term = get_term_by('name', $variation['date'], 'pa_data');
if ( $variation['date'] == $attribute_term ) {
return;
}
$variation_post = array( // Setup the post data for the variation
'post_title' => 'Variation #'.$index.' of data for product#'. $post_id,
'post_name' => 'product-'.$post_id.'-variation-'.$index,
'post_status' => 'publish',
'post_parent' => $post_id,
'post_type' => 'product_variation',
'guid' => home_url() . '/?product_variation=product-' . $post_id . '-variation-' . $index
);
$variation_post_id = wp_insert_post($variation_post); // Insert the variation
// We need to insert the slug not the name into the variation post meta
update_post_meta($variation_post_id, 'attribute_pa_data', $attribute_term->slug);
update_post_meta($variation_post_id, '_price', $variation['price']);
update_post_meta($variation_post_id, '_regular_price', $variation['price']);
}
function ProductSetVariations($post_id ) {
// Get product object
$product = wc_get_product($post_id);
// Verify if the product is variable or simple product
if ( !$product->is_type( 'variable' ) ) {
return;
};
$product_data = array(
'days' => array(
array(
'sku' => '123SKU',
'date' => '1 April 2020',
'price' => '10',
'stock' => '20'
),
array(
'sku' => '456SKU',
'date' => '2 April 2020',
'price' => '10',
'stock' => '20'
),
array(
'sku' => '789SKU',
'date' => '3 April 2020',
'price' => '10',
'stock' => '20'
)
)
);
insert_product_attributes($post_id, $product_data['days']); // Insert variations passing the new post id & variations
insert_product_variations($post_id, $product_data['days']);
}
};
I'm trying to create subscription order by code. I'm using the latest version of Woocommerce and Woocommerce Subscription plugins. It's working fine, but I got different order-pay page when the order contains subscription product.
When the order contains a subscription, I got all the billing and the shipping fields. If the order only contains simple products, I got a summary about the order and the payment methods. I would like the same behavior for subscriptions.
Example link:
https://test.eu/hu/cart/order-pay/4467/?pay_for_order=true&key=wc_order_161651fdsf56
I generate the link with the method get_checkout_payment_url and I got similar link in both cases, but if the order contains a subscription, I 'm redirected to the cart page immediately.
Does anybody faced with same problem?
Thank you for any help!
Best regards,
Mark
$start_date = $order_data['created_at'];
$billing_address = array(
'first_name' => $order_data['first_name'],
'last_name' => $order_data['last_name'],
'company' => $order_data['company_name'],
'email' => $order_data['email'],
'phone' => $order_data['phone_number'],
'address_1' => $order_data['street_address'],
'address_2' => $order_data['apartment_suite_unit'],
'city' => $order_data['town'],
'postcode' => $order_data['zip_code'],
'country' => $order_data['country']
);
$shipping_address = array(
'first_name' => $order_data['shipping_first_name'],
'last_name' => $order_data['shipping_last_name'],
'company' => $order_data['shipping_company_name'],
'address_1' => $order_data['shipping_street_address'],
'address_2' => $order_data['shipping_apartment_suite_unit'],
'city' => $order_data['shipping_town'],
'postcode' => $order_data['shipping_zip_code'],
'country' => $order_data['shipping_country']
);
$email = $order_data['email'];
if (!$user = get_user_by('email', $email)) $user = get_user_by('id', wc_create_new_customer($email));
$order = wc_create_order(array('customer_id' => $user->ID));
update_post_meta($order->ID, "phone_order", true);
foreach ($order_data['ordered_items'] as $item) {
$product = wce_get_product($item, $currency);
$order->add_product( $product, $item['quantity']);
}
$order->set_address( $billing_address, 'billing' );
$order->set_address( $shipping_address, 'shipping' );
$order->set_currency( $currency );
$order->set_payment_method( $order_data['payment_method'] );
$order->calculate_totals();
$order->calculate_taxes();
// Előfizetés
foreach ($order_data['ordered_items'] as $item) {
if (WC_Subscriptions_Product::is_subscription($item['id'])) {
$product = wce_get_product($item, $currency);
$sub = wcs_create_subscription(array('order_id' => $order->id, 'billing_period' => $period, 'billing_interval' => $interval, 'start_date' => $start_date));
$sub->add_product( $product, $item['quantity']/*, $args*/);
$sub->set_address( $billing_address, 'billing' );
$sub->set_address( $shipping_address, 'shipping' );
$sub->calculate_totals();
}
}
$payment_link = $order->get_checkout_payment_url();
return $payment_link;
}
I have created an order using this code. An order is successfully created in the order section of the backend but the webhook is not called.
$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->calculate_totals();
When I create an order normally the delivery URL is pinged successfully and i can see it in the log too.
Any idea how to get the webhook called?
You can manually trigger a hook with do_action():
do_action( 'some_wc_hook' );