Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@blogwerk
Created November 22, 2013 14:16
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save blogwerk/7600543 to your computer and use it in GitHub Desktop.
Save blogwerk/7600543 to your computer and use it in GitHub Desktop.
<?php
/**
* This can be used to manually create a simple order. You can add products and some
* configurations and error pages if anything fails. This will not do an actual checkout
* and can be used to create apps that are used by cashiers.
* @author Michael Sebel <michael.sebel@blogwerk.com>
*/
class WooCommerce_ManualOrder
{
/**
* @var int the order id
*/
protected $orderId = 0;
/**
* @var int total order price. Looks like we need to calculate this ourselves.
*/
protected $orderTotal = 0;
/**
* @var array list of all error pages to do redirects
*/
protected $errorUrls = array();
/**
* @var bool determines if the class should do redirects or error messages
*/
protected $redirectOnErrors = false;
/**
* @var array all purchased products, to recalculate stock
*/
protected $purchasedProducts = array();
/**
* Creates the order post object and saves the userId as billing address
* @param int $userId the user to assign the order to
*/
public function initializeOrder($userId = 0)
{
if ($userId == 0) {
$userId = get_current_user_id();
}
// Create default order data
$orderData = apply_filters('woocommerce_new_order_data', array(
'post_type' => 'shop_order',
'post_title' => sprintf(
__('Bestellung Nr. %s', 'woocommerce'),
strftime(_x( '%b %d, %Y @ %I:%M %p', 'Order date parsed by strftime', 'woocommerce'))
),
'post_status' => 'publish',
'ping_status' => 'closed',
'post_author' => 1,
'post_password' => uniqid('order_')
));
// insert order, post
$this->orderId = wp_insert_post($orderData);
if (is_wp_error($this->orderId)) {
$this->errorHandler('create-order-error');
} else {
do_action('woocommerce_new_order', $this->orderId);
}
// Meta to find the order again
update_post_meta($this->orderId, '_customer_user', $userId);
update_post_meta($this->orderId, '_order_key', apply_filters('woocommerce_generate_order_key', uniqid('order_')));
update_post_meta($this->orderId, '_order_currency', get_woocommerce_currency());
update_post_meta($this->orderId, '_prices_include_tax', get_option( 'woocommerce_prices_include_tax' ));
update_post_meta($this->orderId, '_customer_ip_address', $_SERVER['REMOTE_ADDR']);
update_post_meta($this->orderId, '_customer_user_agent', $_SERVER['HTTP_USER_AGENT']);
// Order total values
foreach (array('_order_shipping', '_order_discount', '_cart_discount', '_order_tax', '_order_shipping_tax', '_order_total') as $key) {
update_post_meta($this->orderId, $key, woocommerce_format_total(0));
}
// Order status
wp_set_object_terms($this->orderId, 'pending', 'shop_order_status' );
}
/**
* Define the various error pages, if anything fails within the order
* @param string $noStock used, if the desired product is not in stock
* @param string $orderCreateError used, if there is a creation error on the order object
*/
public function setErrorPages($noStock, $orderCreateError)
{
$this->errorUrls = array(
'no-stock' => $noStock,
'create-order-error' => $orderCreateError
);
$this->redirectOnErrors = true;
}
/**
* Which payment type is used for the order. This doesn't actually do the payment,
* it is just for displaying later. Normally this is cash.
* @param string $paymentId the payment id from woocommerce
* @throws Exception if initializeOrder wasn't called yet
*/
public function setPaymentId($paymentId)
{
if ($this->orderId > 0) {
// Find out the name of the payment method to create metadata
$gateways = $this->getGateways();
$paymentTitle = '';
foreach ($gateways as $gateway) {
if ($gateway->id == $paymentId) {
$paymentTitle = $gateway->title;
}
}
// Set the metadata for payment
update_post_meta($this->orderId, '_payment_method', $paymentId);
update_post_meta($this->orderId, '_payment_method_title', $paymentTitle);
} else {
throw new Exception('Please call initializeOrder() before setting the payment id');
}
}
/**
* @return array returns all registered payment gateways
*/
public function getGateways()
{
global $woocommerce;
$gateways = $woocommerce->payment_gateways();
return $gateways->payment_gateways;
}
/**
* Adds a product to the order
* @param int $productId the product to add
* @param string $quantity the quantity of the product
* @param array $variation the optional variation of the product
*/
public function addProduct($productId, $quantity, $variation = array())
{
// Did the order already initialize?
if ($this->orderId == 0) {
$this->initializeOrder();
}
$quantity = apply_filters('woocommerce_stock_amount', $quantity);
$product = new WC_Product_Simple($productId);
if ($product->get_stock_quantity() >= $quantity) {
// Add line item
$itemId = woocommerce_add_order_item($this->orderId, array(
'order_item_name' => $product->get_title(),
'order_item_type' => 'line_item'
));
// Calc price and total
$price = $quantity * $product->get_price();
$this->orderTotal += $price;
// Set product id and desired quantity
woocommerce_add_order_item_meta($itemId, '_product_id', $product->id);
woocommerce_add_order_item_meta($itemId, '_qty', $quantity);
woocommerce_add_order_item_meta($itemId, '_tax_class', '');
woocommerce_add_order_item_meta($itemId, '_line_total', $price);
woocommerce_add_order_item_meta($itemId, '_line_subtotal', $price);
// Only add a variation if there is one
if (isset($variation['variation_id']) > 0) {
woocommerce_add_order_item_meta($itemId, '_variation_id', $variation['variation_id']);
woocommerce_add_order_item_meta($itemId, $variation['variation_attr_name'], $variation['variation_attr_name_value']);
}
// line item total values
foreach (array('_line_tax', '_line_subtotal_tax') as $key) {
woocommerce_add_order_item_meta($itemId, $key, woocommerce_format_total(0) );
}
// Add the product to purchased product and add, how many have been bought
$product->temp_sold_quantity = $quantity;
$this->purchasedProducts[] = $product;
} else {
$this->errorHandler('no-stock');
}
}
/**
* Set the billing address
* @param string $firstname the firstname
* @param string $lastname the lastname
* @param string $street the street
* @param string $zip zip of the city
* @param string $city the city
* @param string $email email address
* @param string $company company name
* @param string $address address addition
*/
public function setBillingAddress($firstname, $lastname, $street, $zip, $city, $email = '', $company = '', $address = '')
{
if ($this->orderId > 0) {
$this->setAddress('_billing', $firstname, $lastname, $street, $zip, $city, $email, $company, $address);
}
}
/**
* Set the billing address
* @param string $firstname the firstname
* @param string $lastname the lastname
* @param string $street the street
* @param string $zip zip of the city
* @param string $city the city
* @param string $email email address
* @param string $company company name
* @param string $address address addition
*/
public function setShippingAddress($firstname, $lastname, $street, $zip, $city, $email = '', $company = '', $address = '')
{
if ($this->orderId > 0) {
$this->setAddress('_shipping', $firstname, $lastname, $street, $zip, $city, $email, $company, $address);
}
}
/**
* Set the billing address
* @param string $prefix the metadata prefix
* @param string $firstname the firstname
* @param string $lastname the lastname
* @param string $street the street
* @param string $zip zip of the city
* @param string $city the city
* @param string $email email address
* @param string $company company name
* @param string $address address addition
*/
protected function setAddress($prefix, $firstname, $lastname, $street, $zip, $city, $email, $company, $address)
{
if (strlen(trim($firstname)) > 0) {
update_post_meta($this->orderId, $prefix . '_first_name', $firstname);
}
if (strlen(trim($lastname)) > 0) {
update_post_meta($this->orderId, $prefix . '_last_name', $lastname);
}
if (strlen(trim($street)) > 0) {
update_post_meta($this->orderId, $prefix . '_address_1', $street);
}
if (strlen(trim($zip)) > 0) {
update_post_meta($this->orderId, $prefix . '_postcode', $zip);
}
if (strlen(trim($city)) > 0) {
update_post_meta($this->orderId, $prefix . '_city', $city);
}
if (strlen(trim($email)) > 0) {
update_post_meta($this->orderId, $prefix . '_email', $email);
}
if (strlen(trim($company)) > 0) {
update_post_meta($this->orderId, $prefix . '_company', $company);
}
if (strlen(trim($address)) > 0) {
update_post_meta($this->orderId, $prefix . '_address_2', $address);
}
}
/**
* Finishes the order and returns the order id
* @return int the order id of the finished order
*/
public function finishOrder()
{
$order = new WC_Order($this->orderId);
// Calculate the order total and finish the order
update_post_meta($this->orderId, '_order_total', $this->orderTotal);
$order->payment_complete();
$order->update_status('completed');
return $this->orderId;
}
/**
* Redirects to an error page, if configured or to a default error page
* @param string $id key in errorUrls, if not found, default error
* @throws Exception if there are no error urls configured
*/
protected function errorHandler($id)
{
if ($this->redirectOnErrors) {
if (isset($this->errorUrls[$id])) {
$redirectUrl = $this->errorUrls[$id];
} else {
$redirectUrl = get_permalink() .'?manualOrderError=' . $id;
}
header('Location: ' . $redirectUrl);
exit;
} else {
throw new Exception(__CLASS__ . ' threw an error: ' . $id);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment