Wordpress (Woocommerce extension) - Create new order programmatically - php

I want to create a new order programmatically.
Workflow is simple: After submitting simple form, user will be created and along with that, a new order.
I managed to create a new user and user_id is returned, now I need to assign a new order all in one step.
How can I accomplish this?

Here's how I programmatically create my orders. I largely followed WC_Checkout::create_order() from #pavel's suggestion above. This is directly from a plugin I'm writing so you'll have to adjust where the source data comes form.
// build order data
$order_data = array(
'post_name' => 'order-' . date_format($order_date, 'M-d-Y-hi-a'), //'order-jun-19-2014-0648-pm'
'post_type' => 'shop_order',
'post_title' => 'Order – ' . date_format($order_date, 'F d, Y # h:i A'), //'June 19, 2014 # 07:19 PM'
'post_status' => 'wc-completed',
'ping_status' => 'closed',
'post_excerpt' => $order->note,
'post_author' => $account->user_id,
'post_password' => uniqid( 'order_' ), // Protects the post just in case
'post_date' => date_format($order_date, 'Y-m-d H:i:s e'), //'order-jun-19-2014-0648-pm'
'comment_status' => 'open'
);
// create order
$order_id = wp_insert_post( $order_data, true );
if ( is_wp_error( $order_id ) ) {
$order->errors = $order_id;
} else {
$order->imported = true;
// add a bunch of meta data
add_post_meta($order_id, 'transaction_id', $order->transaction_id, true);
add_post_meta($order_id, '_payment_method_title', 'Import', true);
add_post_meta($order_id, '_order_total', $order->gross, true);
add_post_meta($order_id, '_customer_user', $account->user_id, true);
add_post_meta($order_id, '_completed_date', date_format( $order_date, 'Y-m-d H:i:s e'), true);
add_post_meta($order_id, '_order_currency', $order->currency, true);
add_post_meta($order_id, '_paid_date', date_format( $order_date, 'Y-m-d H:i:s e'), true);
// billing info
add_post_meta($order_id, '_billing_address_1', $order->address_line_1, true);
add_post_meta($order_id, '_billing_address_2', $order->address_line_2, true);
add_post_meta($order_id, '_billing_city', $order->city, true);
add_post_meta($order_id, '_billing_state', $order->state, true);
add_post_meta($order_id, '_billing_postcode', $order->zip, true);
add_post_meta($order_id, '_billing_country', $order->country, true);
add_post_meta($order_id, '_billing_email', $order->from_email, true);
add_post_meta($order_id, '_billing_first_name', $order->first_name, true);
add_post_meta($order_id, '_billing_last_name', $order->last_name, true);
add_post_meta($order_id, '_billing_phone', $order->phone, true);
// get product by item_id
$product = get_product_by_sku( $order->item_id );
if( $product ) {
// add item
$item_id = wc_add_order_item( $order_id, array(
'order_item_name' => $product->get_title(),
'order_item_type' => 'line_item'
) );
if ( $item_id ) {
// add item meta data
wc_add_order_item_meta( $item_id, '_qty', 1 );
wc_add_order_item_meta( $item_id, '_tax_class', $product->get_tax_class() );
wc_add_order_item_meta( $item_id, '_product_id', $product->ID );
wc_add_order_item_meta( $item_id, '_variation_id', '' );
wc_add_order_item_meta( $item_id, '_line_subtotal', wc_format_decimal( $order->gross ) );
wc_add_order_item_meta( $item_id, '_line_total', wc_format_decimal( $order->gross ) );
wc_add_order_item_meta( $item_id, '_line_tax', wc_format_decimal( 0 ) );
wc_add_order_item_meta( $item_id, '_line_subtotal_tax', wc_format_decimal( 0 ) );
}
// set order status as completed
wp_set_object_terms( $order_id, 'completed', 'shop_order_status' );
// if downloadable
if( $product->is_downloadable() ) {
// add downloadable permission for each file
$download_files = $product->get_files();
foreach ( $download_files as $download_id => $file ) {
wc_downloadable_file_permission( $download_id, $product->id, new WC_Order( $order_id ) );
}
}
} else {
$order->errors = 'Product SKU (' . $order->$item_id . ') not found.';
}
}
function get_product_by_sku( $sku ) {
global $wpdb;
$product_id = $wpdb->get_var( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key='_sku' AND meta_value='%s' LIMIT 1", $sku ) );
if ( $product_id ) return new WC_Product( $product_id );
return null;
}
Order Class
The is the interim class I use to store the orders before importing into WordPress / WooCommerce.
class ImportOrder
{
// public vars
public $date;
public $time;
public $time_zone;
public $first_name;
public $middle_name;
public $last_name;
public $type;
public $status;
public $currency;
public $gross;
public $fee;
public $net;
public $note;
public $to_email;
public $from_email;
public $transaction_id;
public $counterparty_status;
public $address_status;
public $item_title;
public $item_id;
public $address_line_1;
public $address_line_2;
public $city;
public $state;
public $zip;
public $country;
public $phone;
public $imported;
public $errors;
}
Add Order
The data here is imported from a PayPal CSV download of historical transactions. The $row variable represents one row in the CSV. You can adjust this to suit your needs.
function add_import_order( $row ) {
// create new order
$order = new ImportOrder();
// done this before?
$order->exists = order_exists( $row[PayPalCols::TRANSACTION_ID] );
// add a bunch of fields
$order->date = $row[PayPalCols::DATE];
$order->time = $row[PayPalCols::TIME];
$order->time_zone = $row[PayPalCols::TIME_ZONE];
$order->type = $row[PayPalCols::TYPE];
$order->status = $row[PayPalCols::STATUS];
$order->currency = $row[PayPalCols::CURRENCY];
$order->gross = $row[PayPalCols::GROSS];
$order->fee = $row[PayPalCols::FEE];
$order->net = $row[PayPalCols::NET];
$order->note = $row[PayPalCols::NOTE];
$order->from_email = $row[PayPalCols::FROM_EMAIL];
$order->to_email = $row[PayPalCols::TO_EMAIL];
$order->transaction_id = $row[PayPalCols::TRANSACTION_ID];
$order->counterparty_status = $row[PayPalCols::COUNTERPARTY_STATUS];
$order->address_status = $row[PayPalCols::ADDRESS_STATUS];
$order->item_title = $row[PayPalCols::ITEM_TITLE];
$order->item_id = $row[PayPalCols::ITEM_ID];
$order->address_line_1 = utf8_encode( $row[PayPalCols::ADDRESS_LINE_1] );
$order->address_line_2 = utf8_encode( $row[PayPalCols::ADDRESS_LINE_2] );
$order->city = utf8_encode( $row[PayPalCols::TOWN_CITY] );
$order->state = utf8_encode( $row[PayPalCols::STATE] );
$order->zip = utf8_encode( $row[PayPalCols::ZIP] );
$order->country = utf8_encode( $row[PayPalCols::COUNTRY] );
$order->phone = utf8_encode( $row[PayPalCols::PHONE] );
return $order;
}

For creating New order, You will have to create Object of WC_Order, If you working outside WooCommerce or in function.php then, First Define Global $woocommerce variable.
So, There will be just 2 line of Code.
global $woocommerce;
$order = new WC_Order( $order_id );
Hope, It will help You.

As of WooCommerce 2.2 (or maybe 2.1 I'm not 100% sure) there is now a function specifically designed for this.
wc_create_order( $args = array() )
with the following default arguments.
$default_args = array(
'status' => '',
'customer_id' => null,
'customer_note' => null,
'order_id' => 0
);
You can see the whole function in the includes/wc-core-functions.php file.

There's a much easier way of doing it, using wc_create_order(). Here's an example, which also adds shipping and product line items. It also creates a Woocommerce subscription, but you can ignore that part for a normal product, the same code will work.
function create_test_sub() {
$email = 'test#test.com';
$start_date = '2015-01-01 00:00:00';
$address = array(
'first_name' => 'Jeremy',
'last_name' => 'Test',
'company' => '',
'email' => $email,
'phone' => '777-777-777-777',
'address_1' => '31 Main Street',
'address_2' => '',
'city' => 'Auckland',
'state' => 'AKL',
'postcode' => '12345',
'country' => 'AU'
);
$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(22998);
$args = array(
'attribute_billing-period' => 'Yearly',
'attribute_subscription-type' => 'Both'
);
$product_variation = $parent_product->get_matching_variation($args);
$product = wc_get_product($product_variation);
// Each variation also has its own shipping class
$shipping_class = get_term_by('slug', $product->get_shipping_class(), 'product_shipping_class');
WC()->shipping->load_shipping_methods();
$shipping_methods = WC()->shipping->get_shipping_methods();
// I have some logic for selecting which shipping method to use; your use case will likely be different, so figure out the method you need and store it in $selected_shipping_method
$selected_shipping_method = $shipping_methods['free_shipping'];
$class_cost = $selected_shipping_method->get_option('class_cost_' . $shipping_class->term_id);
$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( $product, $quantity, $args);
$order->set_address( $address, 'billing' );
$order->set_address( $address, 'shipping' );
$order->add_shipping((object)array (
'id' => $selected_shipping_method->id,
'label' => $selected_shipping_method->title,
'cost' => (float)$class_cost,
'taxes' => array(),
'calc_tax' => 'per_order'
));
$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( $product );
$interval = WC_Subscriptions_Product::get_interval( $product );
$sub = wcs_create_subscription(array('order_id' => $order->id, 'billing_period' => $period, 'billing_interval' => $interval, 'start_date' => $start_date));
$sub->add_product( $product, $quantity, $args);
$sub->set_address( $address, 'billing' );
$sub->set_address( $address, 'shipping' );
$sub->add_shipping((object)array (
'id' => $selected_shipping_method->id,
'label' => $selected_shipping_method->title,
'cost' => (float)$class_cost,
'taxes' => array(),
'calc_tax' => 'per_order'
));
$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>";
}

have a look at my solution:
creating Woocommerce order with line_item programmatically
Works like a charm and goes to the correct WC class that is used by the new REST API

Unfortunately, I don't think there are no easy way to do this I'm afraid.
You need to add the order post using wp_insert_post(); and then add all the meta data using update_post_meta(). You then need to add the using woocommerce_add_order_item() and woocommerce_add_order_item_meta(). Lasty you need to set the order status using wp_set_object_terms().
It's quite a lot of steps and pitfalls. You need to check your database carefully and add all the data and meta data you need to process and complete the order.

in woocommerce WC_Checkout class has a "create_order" method. You can clone WC_Checkout class, give it another name, change the code of the method for your purposes and call like
include 'path_to_Cloned_WC_Checkout';
$chk = new Cloned_WC_Checkout();
$chk->create_order();
in form handler

maybe this way..
function insert_order_to_db($seller,$order_date){
global $wpdb;
$result = $wpdb->query(
"
INSERT INTO `wp_posts`(`ID`, `post_author`, `post_date`, `post_date_gmt`, `post_content`, `post_title`, `post_excerpt`, `post_status`, `comment_status`, `ping_status`, `post_password`, `post_name`, `to_ping`, `pinged`, `post_modified`, `post_modified_gmt`, `post_content_filtered`, `post_parent`, `guid`, `menu_order`, `post_type`, `post_mime_type`, `comment_count`)
VALUES (
17188,
$seller,
'".date_format($order_date, 'Y-m-d H:i:s e')."',
'".date_format($order_date, 'Y-m-d H:i:s e')."',
'no',
'Order –". date_format($order_date, 'F d, Y # h:i A')."',
'noxp',
'wc-completed',
'open',
'close',
'".uniqid( 'order_' )."',
'order-". date_format($order_date, 'M-d-Y-hi-a')."',
'',
'',
'2017-07-24',
'2017-07-24',
'',
0,
'',
0,
'shop_order',
'',
0)
"
);
$order_id = $wpdb->insert_id;
return $order_id;
}
function proccess_order_meta(){
$date = date_create("2017-07-24");
$order_id = insert_order_to_db(194816,$date);
if( is_wp_error( $order_id ) ){
$order->errors = $order_id;
}
else
{
$order_id = 17188;
add_post_meta($order_id, '_payment_method_title', 'پرداخت آنلاین', true);
add_post_meta($order_id, '_order_total', 30000, true);
add_post_meta($order_id, '_customer_user', 194816, true);
add_post_meta($order_id, '_completed_date', date_format( $date, 'Y-m-d H:i:s e'), true);
add_post_meta($order_id, '_paid_date', date_format( $date, 'Y-m-d H:i:s e'), true);
add_post_meta($order_id, '_billing_email', "mavaezi46#gmail.com", true);
add_post_meta($order_id, '_billing_first_name', "علی", true);
}
}
proccess_order_meta();

that line for downloadables is fire.
my files weren't showing up under user downloads without it.
// if downloadable
if( $product->is_downloadable() ) {
// add downloadable permission for each file
$download_files = $product->get_files();
foreach ( $download_files as $download_id => $file ) {
wc_downloadable_file_permission( $download_id, $product->id, new WC_Order( $order_id ) );
}
}

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();
}

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

WooCommerce Subscriptions hook: Get Order Items

WooCommerce 3.0 broke my app and I cannot figure out how to fix it now.
I have an action for when a subscription is added/changed running here:
Inside the function I was getting the order details and finding the line item for a variable subscription to update my custom DB with the option as well as getting custom order meta that I added via woocommerce_form_field:
This no longer works and everything appears protected? How can I update this to work with 3.0?
add_action( 'woocommerce_subscription_status_changed', 'update_subscription', 10, 3 );
function update_subscription( $id, $old_status, $new_status ) {
$sitelink_db = new SSLA_DB_Sitelink();
$order = new WC_Order( $id );
$items = $order->get_items();
$subscription_type = '';
$user_id = $order->get_user_id();
$sitelink_domain = get_post_meta( $order->id, 'ssla_sitelink_url', true );
foreach ($items as $item) {
if( "SiteLink Subscription" === $item['name'] ) {
$subscription_type = $item['brand'];
}
}
$customer_data = array(
'user_id' => $user_id,
'subscription_type' => $subscription_type,
'domain_referrer' => $sitelink_domain,
'active_subscription' => $new_status,
'date_modified' => date( 'Y-m-d H:i:s' ),
);
$sitelink_db->add( $customer_data );
}
Basically I need to get that variation name of the subscription to store in my DB, as well as that custom meta field I made. Which does not work anymore either
Here's my best guess. It's impossible to test since I don't have the same setup as you.
Few notes:
The $subscription object is passed to the woocommerce_subscription_status_changed hook so let's use it.
$order->id should be replaced by $order->get_id() in WC3.0, but we're going to use the the $subscription object (the subscription order class extends the order class so it's similar).
getters must be used on the WC_Order_Item_Product object that is returned when looping through get_items() so $item['name'] becomes $item->get_name()
Here's the full code block:
add_action( 'woocommerce_subscription_status_changed', 'update_subscription', 10, 4 );
function update_subscription( $subscription_id, $old_status, $new_status, $subscription ) {
$match_this_id = 99; // Change this to the product ID of your special subscription
$sitelink_db = new SSLA_DB_Sitelink();
$items = $subscription->get_items();
$subscription_type = '';
$user_id = $subscription->get_user_id();
$sitelink_domain = $subscription->get_meta( 'ssla_sitelink_url' );
foreach ($items as $item) {
if( $match_this_id === $item->get_product_id() ) {
$product = $item->get_product();
if( $product->is_type( 'variation' ) ){
$subscription_type = $product->get_attribute( 'brand' );
}
}
}
$customer_data = array(
'user_id' => $user_id,
'subscription_type' => $subscription_type,
'domain_referrer' => $sitelink_domain,
'active_subscription' => $new_status,
'date_modified' => date( 'Y-m-d H:i:s' ),
);
$sitelink_db->add( $customer_data );
}

Avoiding action Being Triggered Twice in woocommerce_thankyou hook

I am using the woocommerce action below to call a custom function, but for some reason it's being triggered twice on every order. Does anyone know why this could be or how to fix it so that it only calls once per order?
add_action( 'woocommerce_thankyou', 'parent_referral_for_all', 10, 1 );
function parent_referral_for_all( $order_id ) {
....
}
UPDATE
I thought the action was being triggered twice but I'm not so sure now. I'm using this action to add another referral inside the affiliatewp plugin, which is adding twice, yet my echo of "Thank You" only appears once.
Everything is working as intended except that the referral (and it's associated order note) are being added twice.
Any help would be greatly appreciated.
That full function:
function parent_referral_for_all( $order_id ) {
//Direct referral
$existing = affiliate_wp()->referrals->get_by( 'reference', $order_id );
$affiliate_id = $existing->affiliate_id;
//Total amount
if( ! empty( $existing->products ) ) {
$productsarr = maybe_unserialize( maybe_unserialize( $existing->products ) );
foreach( $productsarr as $productarr ) {
$bigamount = $productarr['price'];
}
}
//Parent amount
$parentamount = $bigamount * .1;
$affiliate_id = $existing->affiliate_id;
$user_info = get_userdata( affwp_get_affiliate_user_id( $existing->affiliate_id ) );
$parentprovider = $user_info->referral;
//Affiliate id by username
$userparent = get_user_by('login',$parentprovider);
$thisid = affwp_get_affiliate_id($userparent->ID);
$args = array(
'amount' => $parentamount,
'reference' => $order_id,
'description' => $existing->description,
'campaign' => $existing->campaign,
'affiliate_id' => $thisid,
'visit_id' => $existing->visit_id,
'products' => $existing->products,
'status' => 'unpaid',
'context' => $existing->context
);
$referral_id2 = affiliate_wp()->referrals->add( $args );
echo "Thank you!";
if($referral_id2){
//Add the order note
$order = apply_filters( 'affwp_get_woocommerce_order', new WC_Order( $order_id ) );
$order->add_order_note( sprintf( __( 'Referral #%d for %s recorded for %s', 'affiliate-wp' ), $referral_id2, $parentamount, $parentamount ) );
}
}
add_action( 'woocommerce_thankyou', 'parent_referral_for_all', 10, 1 );
To avoid this repetition, you couldadd a custom post meta data to the current order, once your "another" referral has been added the first time.
So your code will be:
function parent_referral_for_all( $order_id ) {
## HERE goes the condition to avoid the repetition
$referral_done = get_post_meta( $order_id, '_referral_done', true );
if( empty($referral_done) ) {
//Direct referral
$existing = affiliate_wp()->referrals->get_by( 'reference', $order_id );
$affiliate_id = $existing->affiliate_id;
//Total amount
if( ! empty( $existing->products ) ) {
$productsarr = maybe_unserialize( maybe_unserialize( $existing->products ) );
foreach( $productsarr as $productarr ) {
$bigamount = $productarr['price'];
}
}
//Parent amount
$parentamount = $bigamount * .1;
$affiliate_id = $existing->affiliate_id;
$user_info = get_userdata( affwp_get_affiliate_user_id( $existing->affiliate_id ) );
$parentprovider = $user_info->referral;
//Affiliate id by username
$userparent = get_user_by('login',$parentprovider);
$thisid = affwp_get_affiliate_id($userparent->ID);
$args = array(
'amount' => $parentamount,
'reference' => $order_id,
'description' => $existing->description,
'campaign' => $existing->campaign,
'affiliate_id' => $thisid,
'visit_id' => $existing->visit_id,
'products' => $existing->products,
'status' => 'unpaid',
'context' => $existing->context
);
$referral_id2 = affiliate_wp()->referrals->add( $args );
echo "Thank you!";
if($referral_id2){
//Add the order note
$order = apply_filters( 'affwp_get_woocommerce_order', new WC_Order( $order_id ) );
$order->add_order_note( sprintf( __( 'Referral #%d for %s recorded for %s', 'affiliate-wp' ), $referral_id2, $parentamount, $parentamount ) );
## HERE you Create/update your custom post meta data to avoid repetition
update_post_meta( $order_id, '_referral_done', 'yes' )
}
}
}
add_action( 'woocommerce_thankyou', 'parent_referral_for_all', 10, 1 );
I hope this will help.
You can check for the existence of the _thankyou_action_done post_meta key like so:
<?php
/**
* Allow code execution only once when the hook is fired.
*
* #param int $order_id The Woocommerce order ID.
* #return void
*/
function so41284646_trigger_thankyou_once( $order_id ) {
if ( ! get_post_meta( $order_id, '_thankyou_action_done', true ) ) {
// Your code here.
}
}
add_action( 'woocommerce_thankyou', 'so41284646_trigger_thankyou_once', 10, 1 );

WPSC_UPDATE_META Function in PHP How too use it?

Someone that's familiar with this know how to use it? I can't find any documentation on this function anywhere. and can this be used to update say for example the weight field and changing it from the default of pounds to options?
I assume the proper setup would be something like this, bu I've no idea what goes in type.
wpsc_update_meta($post_id, 'weight', '3', $type);
wpsc_update_meta($post_id, 'weight_unit', 'ounce', $type);
Any ideas guys? The full function is listed below.
function wpsc_update_meta( $object_id = 0, $meta_key, $meta_value, $type, $global = false ) {
global $wpdb;
if ( !is_numeric( $object_id ) || empty( $object_id ) && !$global ) {
return false;
}
$cache_object_id = $object_id = (int) $object_id;
$object_type = $type;
$meta_key = wpsc_sanitize_meta_key( $meta_key );
$meta_tuple = compact( 'object_type', 'object_id', 'meta_key', 'meta_value', 'type' );
$meta_tuple = apply_filters( 'wpsc_update_meta', $meta_tuple );
extract( $meta_tuple, EXTR_OVERWRITE );
$meta_value = $_meta_value = maybe_serialize( $meta_value );
$meta_value = maybe_unserialize( $meta_value );
$cur = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM `".WPSC_TABLE_META."` WHERE `object_type` = %s AND `object_id` = %d AND `meta_key` = %s", $object_type, $object_id, $meta_key ) );
if ( !$cur ) {
$wpdb->insert( WPSC_TABLE_META, array( 'object_type' => $object_type, 'object_id' => $object_id, 'meta_key' => $meta_key, 'meta_value' => $_meta_value ) );
} elseif ( $cur->meta_value != $meta_value ) {
$wpdb->update( WPSC_TABLE_META, array( 'meta_value' => $_meta_value), array( 'object_type' => $object_type, 'object_id' => $object_id, 'meta_key' => $meta_key ) );
}
wp_cache_delete( $cache_object_id, $object_type );
if ( !$cur ) {
return true;
}
}
I'm not familiar with e-Commerce plugin, but I assume that this function is some kind of their own implementation of the WP update_metadata() function.
If that's true, then $type param should have the same meaning as the $meta_type param at update_metadata() (e.g. post_type, taxonomy...). Also look at the wp_cache_delete() usage at the bottom. This could give you some additional hints.

Categories