How to create order Manually in woocommerce? - php

i had develop a phonegap application with my woocommerce store.
now i want to create order manually.
So anyone can tell me when new order is created in woocommerce store how many data inserted in which tables in database. i mean data at the time of new order generation in which table and in which fashion are they stored. Please help

A order in WooCommerce is stored as as WordPress post with post_type = 'shop_order' in the table wp_posts.
Each order item in the order is stored in wp_woocommerce_order_items, with additional information about each order item line in the table wp_woocommerce_order_itemmeta.
To add a new order:
Insert a new order into the wp_posts table
Add the requested order lines into wp_woocommerce_order_items
For each of the order lines, add the order line details into wp_woocommerce_order_itemmeta.
Example data from a MySQL dump:
INSERT INTO wp_posts VALUES (38,1,'2014-06-09 10:02:00','2014-06-09 10:02:00','','Order – June 9, 2014 # 10:02 AM','','publish','closed','closed','','order','','','2014-06-09 10:02:43','2014-06-09 10:02:43','',0,'http://example.com/?post_type=shop_order&p=38',0,'shop_order','',0);
INSERT INTO wp_woocommerce_order_items VALUES (33,'My product','line_item',38);
INSERT INTO wp_woocommerce_order_itemmeta VALUES (167,33,'_qty','1');
INSERT INTO wp_woocommerce_order_itemmeta VALUES (168,33,'_tax_class','');
INSERT INTO wp_woocommerce_order_itemmeta VALUES (169,33,'_product_id','20');
INSERT INTO wp_woocommerce_order_itemmeta VALUES (170,33,'_variation_id','');
INSERT INTO wp_woocommerce_order_itemmeta VALUES (171,33,'_line_subtotal','13');
INSERT INTO wp_woocommerce_order_itemmeta VALUES (172,33,'_line_subtotal_tax','');
INSERT INTO wp_woocommerce_order_itemmeta VALUES (173,33,'_line_total','13');
INSERT INTO wp_woocommerce_order_itemmeta VALUES (174,33,'_line_tax','');
I would recommend that you try manually creating an order in the database with the settings you want the generated order to have, that way you see the relations and the various settings that you will need to set manually.

I spent many hours on this code - so I want to share it.
It creates a new order, with one product, adds shipment to the order with the flat rate of shipment, by the postcode of the customer.
I use it to create subscription orders for my clients.
function create_order($user_id, $product_id)
{
$order = wc_create_order();
$order->set_created_via("subscription");
$product = wc_get_product($product_id);
$order->add_product($product, $quantity);
$order_id = $order->get_id();
$postcode = get_user_meta($user_id, 'shipping_postcode', true);
$package = array('destination' => array('country' => 'IL', 'postcode' => $postcode));
$zone = WC_Shipping_Zones::get_zone_matching_package($package);
$method = WC_Shipping_Zones::get_shipping_method( $zone->get_id() );
This was the hardest to find out. The get_shipping_method returns class name. The next line create instance of it.
$shipping = new $method;
Now get the price of the shipment:
$rate = new WC_Shipping_Rate();
$rate->id = 'flat_rate';
$rate->label = 'shipment';
$rate->cost = $shipping->cost;
$rate->calc_tax = 'per_order'; // Not sure about this one
// Adding it to the order
$order->add_shipping($rate);
$order->calculate_totals();
// assign the order to the current user
update_post_meta($order->get_id(), '_customer_user', $user_id);
// payment_complete
$order->payment_complete();`
Copy billing and shipment info
foreach (array('billing_first_name',
'billing_last_name',
'billing_phone',
'billing_address_1',
'billing_address_2',
'shipping_first_name',
'shipping_last_name',
'shipping_address_1',
'shipping_address_2',
'shipping_city',
'shipping_postcode') as $key) {
$values = get_user_meta($user_id, $key);
update_post_meta($order_id, "_" . $key, $values[0]);
}

Related

WooCommerce simple ticketing system at product level

I am using WordPress and WooCommerce and have an SQL query to call meta tags for a specific product type.
function get_last_order_id_from_product( $product_id ) {
global $wpdb;
return $wpdb->get_col( $wpdb->prepare( "
SELECT oi.order_id
FROM {$wpdb->prefix}woocommerce_order_items as oi
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as oim
ON oi.order_item_id = oim.order_item_id
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as oim2
ON oi.order_item_id = oim2.order_item_id
LEFT JOIN {$wpdb->posts} AS p
ON oi.order_id = p.ID
WHERE p.post_type = 'shop_order'
AND oi.order_item_type = 'line_item'
AND oim.meta_key = '_product_id'
AND oim.meta_value = '%d'
AND oim2.meta_key = 'Ticket Number'
ORDER BY SUBSTRING_INDEX(oim2.meta_value, ' ', 10) DESC
LIMIT 1
", $product_id ) );
}
The code above takes a product id and outputs the meta tag, my issue arose when I realized that when a user buys more than a single quantity the meta value it creates for the 'Ticket Number' key is '10 11' which mysql views as a single string. I tried breaking it apart using SUBSTRING_INDEX as shown above but it wont view any orders past the first.
I need it to view the numbers as individual numbers so if a user buys ten of the item it can recognise the last number as the highest (say 21 22 23 24) and the next user to buy this product the sql query will recognise the 24 from the string as the highest number and make the new items meta be 25.
Here is my function as well if it is of any use, it currently runs but is only working from the meta value of 1 however many of the product is bought:
add_action('woocommerce_order_status_processing', 'action_woocommerce_order_status_completed');
function action_woocommerce_order_status_completed($order_id) {
$order = new WC_Order($order_id);
$items = $order->get_items();
foreach ($items as $key => $value) {
$get_last_order = get_last_order_id_from_product( 10 );
if ($get_last_order == null){
$gen_id_1 = "0";
} else {
$get_order_id = implode($get_last_order);
$lastorder = wc_get_order($get_order_id);
$lastitem = $lastorder->get_items();
foreach ($lastitem as $key2 => $value2) {
$custom_thing = $value2->get_meta('Ticket Number');
}
$gen_ids = explode(' ', $custom_thing);
$gen_id_1 = end($gen_ids);
}
$qua = $value->get_quantity();
for ($x = 1; $x <= $qua; $x++) {
$gen_id_1++;
$gen_id_2 .= " $gen_id_1";
};
$value->add_meta_data( "Ticket Number", $gen_id_2);
$value->save();
}
}
There is a different way, much more easy and efficient to make that work.
When an order change to processing status:
First, for each order item, I increase the number of tickets sold (item quantity) as an index at the product level (saved/updated as custom product meta data).
Then for each order item, I generate from the related product index (before updating it) the tickets numbers based on the item quantity, that I save as custom order item meta data.
And to finish, for each order item, I update the related product "index" adding the quantity sold to the current "index" value.
The code (commented):
add_action( 'woocommerce_order_status_processing', 'action_woocommerce_order_status_processing', 10, 2 );
function action_woocommerce_order_status_processing( $order_id, $order ) {
// Loop through order items
foreach ($order->get_items() as $item_id => $item ) {
// Check that tickets numbers haven't been generated yet for this item
if( $item->get_meta( "_tickets_number") )
continue;
$product = $item->get_product(); // Get the WC_Produt Object
$quantity = (int) $item->get_quantity(); // Get item quantity
// Get last ticket sold index from product meta data
$now_index = (int) $product->get_meta('_tickets_sold');
$tickets_ids = array(); // Initializing
// Generate an array of the customer tickets Ids from the product registered index (last ticket ID)
for ($i = 1; $i <= $quantity; $i++) {
$tickets_ids[] = $now_index + $i;
};
// Save the tickets numbers as order item custom meta data
$item->update_meta_data( "Tickets numbers", implode(' ', $tickets_ids) ); // Displayed string of tickets numbers on customer orders and emails
$item->update_meta_data( "_tickets_number", $tickets_ids ); // (Optional) Array of the ticket numbers (Not displayed to the customer)
$item->save(); // Save item meta data
// Update the Ticket index for the product (custom meta data)
$product->update_meta_data('_tickets_sold', $now_index + $quantity );
$product->save(); // Save product data
}
$order->save(); // Save all order data
}
Code goes in functions.php file of your active child theme (active theme). Tested and works.
The _tickets_number hidden custom order item meta data is optional and allow you to get the array of tickets, instead of a string of tickets using: $item->get_meta('_tickets_number');
If you want a global ticket system (not at product level) you will use the following instead:
add_action( 'woocommerce_order_status_processing', 'action_woocommerce_order_status_processing', 10, 2 );
function action_woocommerce_order_status_processing( $order_id, $order ) {
// Loop through order items
foreach ($order->get_items() as $item_id => $item ) {
$now_index = (int) get_option( "wc_tickets_number_index"); // Get last ticket number sold (globally)
$quantity = (int) $item->get_quantity(); // Get item quantity
$tickets_ids = array(); // Initializing
// Generate an array of the customer tickets Ids from the tickets index
for ($i = 1; $i <= $quantity; $i++) {
$tickets_ids[] = $now_index + $i;
};
// Save the tickets numbers as order item custom meta data
$item->update_meta_data( "Tickets numbers", implode(' ', $tickets_ids) ); // Displayed string of tickets numbers on customer orders and emails
$item->update_meta_data( "_tickets_number", $tickets_ids ); // (Optional) Array of the ticket numbers (Not displayed to the customer)
$item->save(); // Save item meta data
// Update last ticket number sold (globally)
update_option( 'wc_tickets_number_index', $now_index + $quantity );
}
$order->save(); // Save all order data
}
Code goes in functions.php file of your active child theme (active theme). It should work.

Fetching part of SQL query for a WHILE loop on php

Trying to fetch part of the query for a while loop.
I have an INNER JOIN sql query using four tables.
On the first while i'm fetching the orders AKA mod_orders
On the second loop i'm trying to fetch the order products with the information from the other tables AKA mod_orders_pords (pords intended).
so my first while needs to use only SELECT * FROM mod_orders
and the second while needs to loop the rest without the grouping part.
The question is: how do i run the second while?
SELECT * FROM mod_orders AS mo
INNER JOIN mod_orders_pords AS mop
ON mo.`id` = mop.`order_id`
INNER JOIN mod_products_list AS mpl
ON mop.`prod_id` = mpl.`id`
INNER JOIN mod_products AS mp
ON mpl.`id` = mp.`page_id`
GROUP BY mop.`order_id`
ORDER BY mo.`id` DESC
The question is not clear and doesn't specify the issue. I think you need to group all orders with product and product info.
First it is better to avoid * specially if you will access the row using the field name as all tables have id field.
You have order -> orderProduct -> product, so you need to sort by order_id then product id.
Then you will create one while loop and start building your array of objects, see sample code as example.
<?php
//I assume the you have result object using your sql query
$order = new stdClass();
$data = array(); //this array will contains all orders
$order->id = null;
while($row = $result->fetch_assoc()){
if(is_null($order->id) || $order->id != $row['order_id'])
{
//this means that we need to create new order->object
$order = new stdClass();
$order->$row['order_id'];
// add more data like order value etc..
$order->products = array();
$data[] = &$order; //add order by refrence
unset($product);
$product = new stdClass();
$product->id =null;
}
if(is_null($product->id) || $product->id != $row['product_id']){
unset($product);
$product = new stdClass();
$product->id = $row['product_id'];
// add more data like product name etc..
$order->products[] = &$product;
}
}
// unset all objects
unset($order, $product);
using this code you can achieve what you need but remember to sort by order, so first sort by order_id then sort by product_id. order by order_id, product_id. If you have third level added to the order by statement and no need to use group by is this case.

Update all WooCommerce product prices to 2 decimals in database

Many of my products have prices like £3.4560 etc. I need them to rounded and stripped to just 2 in the database.
The output of 2 decimal places in WooCommerce is not enough and fails to work with a Tax Toggle plugin.
Is there a database query that can do this?
I've seen some bits on Rounding and Truncate but not sure how to execute this as my knowledge is poor.
Any help is appreciated.
Update 2: (Added some code to clear all related product transient cache)
Here is some code that will update all the product prices (make a database backup before):
global $wpdb;
$postmeta = $wpdb->prefix . "postmeta";
$posts = $wpdb->prefix . "posts";
// 1. First query: Get all prices
$results = $wpdb->get_results( "
SELECT $postmeta.*
FROM $postmeta
INNER JOIN $posts ON $postmeta.post_id = $posts.ID
WHERE $posts.post_type LIKE '%product%'
AND $postmeta.meta_key LIKE '%price%'
AND $postmeta.meta_value != ''
ORDER BY $postmeta.meta_id ASC
" );
// iterating through each price and update it
foreach($results as $result){
$meta_id = $result->meta_id;
$post_id = $result->post_id;
$meta_key = $result->meta_key;
$meta_value = number_format( $result->meta_value, 2 );
// 2. Udating prices query
$wpdb->query( $wpdb->prepare( "
UPDATE $postmeta
SET meta_id = $meta_id, post_id = $post_id, meta_key = '$meta_key', meta_value = '$meta_value'
WHERE $postmeta.meta_id = %d
", $meta_id ) );
// 3. Clear all related product transient cached data (refresh prices)
wc_delete_product_transients($post_id);
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Once saved, just browse any page of your site, check your database wp_postmeta table searching for ‰price‰ LIKE meta_key. Now you can remove this code.

WooCommerce - Get the number of orders by a given customer

I need to find that a particular customer has done business with that store previously.
To achieve that I need to find the number of orders by a given customer.
How can I achieve that?
I tried Googling, but did not find any solution.
Just drop in the user id and you'll get your total number of orders:
$numorders = wc_get_customer_order_count( $userid );
To go a step further for my own purposes, I use this code to get the number of a customer's non-cancelled orders since I don't want to count failed order attempts:
// Get TOTAL number of orders for customer
$numorders = wc_get_customer_order_count( $userid );
// Get CANCELLED orders for customer
$args = array(
'customer_id' => $userid,
'post_status' => 'cancelled',
'post_type' => 'shop_order',
'return' => 'ids',
);
$numorders_cancelled = 0;
$numorders_cancelled = count( wc_get_orders( $args ) ); // count the array of orders
// NON-CANCELLED equals TOTAL minus CANCELLED
$num_not_cancelled = $numorders - $numorders_cancelled;
Modifying #MarkPraschan Code, which works well for me without throwing any notice, as i got 2 notice about undefined variable $user_id and i'm not passing the code through a function. Using below code worked for me (which gets the number of order transaction minus canceled orders);
$current_user = wp_get_current_user();
$numorders = wc_get_customer_order_count( $current_user->ID );
// Get CANCELLED orders for customer
$args = array(
'customer_id' => $current_user->ID,
'post_status' => 'cancelled',
'post_type' => 'shop_order',
'return' => 'ids',
);
$numorders_cancelled = 0;
$numorders_cancelled = count( wc_get_orders( $args ) ); // count the array of orders
// NON-CANCELLED equals TOTAL minus CANCELLED
$num_not_cancelled = $numorders - $numorders_cancelled;
if you intend to display both completed and non completed orders, you will use the first two line of the above code, which is;
$current_user = wp_get_current_user();
$numorders = wc_get_customer_order_count( $current_user->ID );
Tested and working on;
WP = v4.9.9
WC = v3.5.3
I know this is an old question, but thought I'd share my code/info anyways.
The customer will be connected to the order via the postmeta key _customer_user in the wp_postmeta table.
You can lookup all orders with the status completed and processing for a specific user ID with the following query, where 279 is the user ID:
SELECT COUNT(p.ID)
FROM wp_posts AS p
INNER JOIN wp_postmeta AS m ON m.post_id = p.ID AND m.meta_key = '_customer_user' AND m.meta_value = 279
WHERE p.post_status IN ('wc-completed', 'wc-processing') AND p.post_type = 'shop_order'
When translated into PHP code that can be used on any WP installation simply by placing the code at the bottom of your theme functions.php file.
This example displays the total orders for a customer on the order page in the back-end of WordPress, directly below the customer selection/dropdown. For instance if you need to know if this is the first order a customer has placed/done you can display a message. Obviously you will want to change the order status filter to something that suits your needs. Doing a direct query like below is more efficient I believe.
// Hook into the order back-end WooCommerce > Orders > [Edit]
// The output will be placed under the dropdown to choose/connect a customer to the order
add_action('woocommerce_admin_order_data_after_order_details', 'f4d_customer_order_count');
function f4d_customer_order_count($order){
global $wpdb;
// Retrieve customer ID based on current order
$customerId = $order->get_customer_id();
// When the order was placed by a guest, just return
if($customerId===0) return;
// When the order is connected to a user/customer, query the total orders
// Database tables we will use in our query (also grab the table prefix)
$postsTable = $wpdb->prefix.'posts';
$postsMetaTable = $wpdb->prefix.'postmeta';
// Filter orders by specific status
$orderStatusFilter = array('wc-completed', 'wc-processing');
// Connect the array into a string that is compatible with our query (IN() query statement)
$orderStatusFilter = "'".implode("','", $orderStatusFilter)."'";
// Only get the single variable from the database
$totalOrders = $wpdb->get_var("
SELECT COUNT(p.ID)
FROM $postsTable AS p
INNER JOIN $postsMetaTable AS m ON m.post_id = p.ID AND m.meta_key = '_customer_user' AND m.meta_value = $customerId
WHERE p.post_status IN ($orderStatusFilter) AND p.post_type = 'shop_order'");
echo '<p class="form-field form-field-wide wc-customer-order-count">';
if($totalOrders===1){
// When this is the first order, display a message to our admin to give a first time offer
echo '<span style="color:white;background-color:red;padding:20px;">FIRST TIME OFFER</span>';
}else{
// Otherwise just display the total orders the customer has placed in the past
echo '<span>'.esc_html__( 'Total Orders', 'super-forms' ) . ': '.$totalOrders.'</span>';
}
echo '</p>';
}
If you need a list format of multiple customers/users, then you can use the $wpdb->get_results() instead of $wpdb->get_var() and loop over the results (table rows).
Found a way.
$args = [
'author' => 'id',
'post_status' => 'any',
'post_type' => 'shop_order'
];
$query = new WP_Query($args);
$orderCountByCustomer = $query->found_posts;

Magento - Set Original Price in sales order

I have create sales order programmatically.Sales order is generated successfully with all prices, but the Original price column is getting blank. I tried to set in Quote object as well as Order object but no luck.
Can you tell me how can I set original price in sales order.
Thanks!
CODE--
$productModel = Mage::getModel('catalog/product');
$productObj = $productModel->setStore($storeId)->setStoreId($storeId)->load($key);
$productObj->setSkipCheckRequiredOption(true);
$quoteItem = Mage::getModel('sales/quote_item')->setProduct($productObj);
$product_price = $productModel->getPrice();
$quoteItem->setOriginalCustomPrice($product_price);
$quoteItem->setOriginalPrice('2.2'); //NOT SET IN DB
$quoteItem->setCustomPrice($product_price);
$quoteItem->setQty($qty);
$quoteItem->setQuote($quoteObj);
$quoteObj->addItem($quoteItem);
$productObj->unsSkipCheckRequiredOption();
$quoteItem->checkData();
$quoteObj->getShippingAddress()->setCollectShippingRates(true);
$quoteObj->getShippingAddress()->collectShippingRates();
$quoteObj->collectTotals();
$quoteObj->setIsActive(0);
$quoteObj->save();

Categories