Created
September 2, 2016 20:20
-
-
Save wluisi/6707ddc285199fdaef65327365248890 to your computer and use it in GitHub Desktop.
Programmatically Create Drupal Commerce Order
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Creates an order for the given user containing the product with the given id. | |
* This example may be extended easily to create an order containing several | |
* products, a shipping and so far. | |
* | |
* Returns a commerce order that's now saved in the database with the status | |
* "processing and can be finished by an administrator in the UI. | |
* Throws an exception, if something is wrong. | |
* | |
* @param integer $uid The numeric user id, e.g. "1" | |
* @param integer $product_id The numeric product id (NOT SKU! ;)) | |
* @return CommerceORder | |
* @throws Exception | |
*/ | |
function my_module_api_create_order($uid, $product_id) { | |
if (empty($uid) || !is_numeric($uid)) { | |
throw new Exception('User ID may not be empty and must be numeric!'); | |
} | |
$user = user_load($uid); | |
if (empty($product_id) || !is_numeric($product_id)) { | |
throw new Exception('Product ID may not be empty and must be numeric!'); | |
} | |
// Use a transaction to ensure that no broken order is created! | |
$transaction = db_transaction(); | |
try { | |
// Create the new order in checkout; you might also check first to | |
// see if your user already has an order to use instead of a new one. | |
$order = commerce_order_new($user->uid, 'checkout_checkout'); | |
// Save the order to get its ID. | |
commerce_order_save($order); | |
// Get the order wrapper which provides helper functionality | |
$order_wrapper = entity_metadata_wrapper('commerce_order', $order); | |
// Add address profiles. | |
// IMPORTANT! The address MUST be set before the line item to | |
// correctly determine the country of supply. | |
// IMPORTANT2: This address setup is made based on single fields in the user | |
// account. If you already use an address field in the user account | |
// this is much simpler and you should have a look at the | |
// commerce_extra module for address fields ;) | |
// Billing | |
// Check that we have at least all REQUIRED fields! | |
if (empty($user->field_firstname[LANGUAGE_NONE][0]['value'])) { | |
throw new Exception('field_firstname may not be empty!'); | |
} | |
if (empty($user->field_lastname[LANGUAGE_NONE][0]['value'])) { | |
throw new Exception('field_lastname may not be empty!'); | |
} | |
if (empty($user->field_city[LANGUAGE_NONE][0]['value'])) { | |
throw new Exception('field_city may not be empty!'); | |
} | |
if (empty($user->field_zip[LANGUAGE_NONE][0]['value'])) { | |
throw new Exception('field_zip may not be empty!'); | |
} | |
if (empty($user->field_street[LANGUAGE_NONE][0]['value'])) { | |
throw new Exception('field_street may not be empty!'); | |
} | |
if (empty($user->field_country[LANGUAGE_NONE][0]['value'])) { | |
throw new Exception('field_country may not be empty!'); | |
} | |
// We have to get or create the first order profile of the users to set the values into. | |
// We use a helper function for that! | |
$billing = _my_module_api_create_order_get_first_customer_profile('billing', $user); | |
$customer_profile_billing_array = array( | |
'country' => isset($user->field_country[LANGUAGE_NONE][0]['value']) ? $user->field_country[LANGUAGE_NONE][0]['value'] : NULL, // DE | |
'name_line' => NULL, // Testperson | |
'first_name' => isset($user->field_firstname[LANGUAGE_NONE][0]['value']) ? $user->field_firstname[LANGUAGE_NONE][0]['value'] : NULL, // Max | |
'last_name' => isset($user->field_lastname[LANGUAGE_NONE][0]['value']) ? $user->field_lastname[LANGUAGE_NONE][0]['value'] : NULL, // Mustermann | |
'organisation_name' => isset($user->field_firma[LANGUAGE_NONE][0]['value']) ? $user->field_firma[LANGUAGE_NONE][0]['value'] : NULL, // Muster AG | |
'administrative_area' => NULL, | |
'sub_administrative_area' => NULL, | |
'locality' => isset($user->field_wohnort[LANGUAGE_NONE][0]['value']) ? $user->field_wohnort[LANGUAGE_NONE][0]['value'] : NULL, // Musterstadt | |
'dependent_locality' => NULL, | |
'postal_code' => isset($user->field_zip[LANGUAGE_NONE][0]['value']) ? $user->field_zip[LANGUAGE_NONE][0]['value'] : NULL, // 32457 | |
'thoroughfare' => isset($user->field_street[LANGUAGE_NONE][0]['value']) ? $user->field_street[LANGUAGE_NONE][0]['value'] : NULL, // Musterstraße 10 | |
'premise' => NULL, | |
'sub_premise' => NULL, | |
'data' => NULL, | |
); | |
// Set the values in the profile entity and attache it to the order | |
$billing->commerce_customer_address[LANGUAGE_NONE][0] = $customer_profile_billing_array; | |
commerce_customer_profile_save($billing); | |
$order->commerce_customer_billing[LANGUAGE_NONE][0]['profile_id'] = $billing->profile_id; | |
// This is only required if you use the commerce_vat module and you want to | |
// set the vat id programmatically with implications on the price calculation! | |
if (!empty($user->field_vat_number[LANGUAGE_NONE][0]['value'])) { | |
$eu_vat_rc = _my_module_api_create_order_get_first_customer_profile('eu_vat_rc', $user); | |
$customer_profile_vat_array = array( | |
'value' => !empty($user->field_vat_number[LANGUAGE_NONE][0]['value']) ? $user->field_vat_number[LANGUAGE_NONE][0]['value'] : NULL, | |
); | |
$eu_vat_rc->commerce_vat_number[LANGUAGE_NONE][0] = $customer_profile_vat_array; | |
commerce_customer_profile_save($eu_vat_rc); | |
$order->commerce_customer_eu_vat_rc[LANGUAGE_NONE][0]['profile_id'] = $eu_vat_rc->profile_id; | |
} | |
// IMPORTANT: If we also use shipping address, this would be the | |
// place to set it here like we already did in "billing"! | |
// Save the order again to save the address attached. | |
$order_wrapper->save(); | |
// Load whatever product represents the item the customer will be | |
// paying for and create a line item for it. | |
$product = commerce_product_load($product_id); | |
// Create a new line item with multiplicity = 1 | |
$line_item = commerce_product_line_item_new($product, 1, $order->order_id); | |
// You may set line item properties here. | |
// Calculate the sell price! | |
rules_invoke_event('commerce_product_calculate_sell_price', $line_item); | |
// Save the line item | |
commerce_line_item_save($line_item); | |
// Attach the line item to the order using the order wrapper. | |
$order_wrapper->commerce_line_items[] = $line_item; | |
$order_wrapper->save(); | |
// Now calculate the total price and save the order again. | |
commerce_order_calculate_total($order); | |
$order_wrapper->save(); | |
// To create the payment we need some information from the order: | |
$total = $order_wrapper->commerce_order_total->amount->value(); | |
$currency_code = $order_wrapper->commerce_order_total->currency_code->value(); | |
$charge = array( | |
'amount' => $total, | |
'currency_code' => $currency_code, | |
); | |
// The following is an example for payment methods commerce_directdebit and bank_transfer. | |
// You may use this as example for other payment types! | |
// SEPA if we have the required users direct debit fields, otherwise bank transfer! | |
if (!empty($user->field_accountholder[LANGUAGE_NONE][0]['value']) && !empty($user->field_bic[LANGUAGE_NONE][0]['value']) && !empty($user->field_iban[LANGUAGE_NONE][0]['value'])) { | |
// SEPA | |
$payment_method = array( | |
'instance_id' => 'commerce_directdebit|commerce_payment_commerce_directdebit', | |
); | |
commerce_directdebit_transaction($payment_method, $order, $charge, $user->field_accountholder[LANGUAGE_NONE][0]['value'], '', '', 1, $user->field_bic[LANGUAGE_NONE][0]['value'], $user->field_iban[LANGUAGE_NONE][0]['value']); | |
} | |
else { | |
// BANK TRANSFER | |
$payment_method = array( | |
'instance_id' => 'bank_transfer|commerce_payment_bank_transfer', | |
); | |
commerce_bank_transfer_transaction($payment_method, $order, $charge); | |
} | |
// Set the payment id in the order array to make it available for invoicing | |
// (no idea why we have to do that manually here. Perhaps there's a better way?) | |
$order->data['payment_method'] = $payment_method['instance_id']; | |
$order_wrapper->save(); | |
// Update the status to processing to allow manual finishing later on. | |
commerce_order_status_update($order, 'processing'); | |
// No explicit transaction commit wanted by drupal... | |
} catch (Exception $e) { | |
// Something went wrong! We don't want to save a broken order to the database | |
// at all. So let's roll back the transaction: | |
$transaction->rollback(); | |
// Add some information to the exception for easier debugging in an outer try / catch! | |
$e->user_id = $uid; | |
$e->product_id = $product_id; | |
// We've done what we could. Re-throw the exception and let an outer | |
// try/catch handle it for error logging. | |
throw $e; | |
} | |
// Yeah, the order was created successfully. Provide it to the outer call! :) | |
return $order; | |
} | |
/** | |
* Helper function to get the users already existing (as implemented: first) | |
* commerce customer profile and return it. | |
* If the user has no customer profile yet, create a new one and return that. | |
* | |
* @param string $type The type of the customer profile e.g. "billing". | |
* @param stdClass $user The user object. | |
* @return stdClass The CommerceCustomerProfile object. | |
*/ | |
function _my_module_api_create_order_get_first_customer_profile($type, stdClass $user) { | |
$query = new EntityFieldQuery(); | |
$query->entityCondition('entity_type', 'commerce_customer_profile') | |
->propertyCondition('uid', $user->uid) | |
->propertyCondition('type', $type); | |
$results = $query->execute(); | |
if (!empty($results['commerce_customer_profile'])) { | |
// Profile already exists. Load! | |
$profile_info = reset($results['commerce_customer_profile']); | |
return commerce_customer_profile_load($profile_info->profile_id); | |
} | |
else { | |
// No profile yet. Create one! | |
return commerce_customer_profile_new($type, $user->uid); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment