Ejemplo n.º 1
0
		protected function editOrderSaveAction()
		{
			if (empty($_POST['quoteSession'])) {
				exit;
			}

			$quoteSession = $_POST['quoteSession'];

			/** @var ISC_QUOTE */
			$quote = getClass('ISC_ADMIN_ORDERS')->getQuoteSession($quoteSession);
			if(!$quote) {
				$this->sendEditOrderNoQuoteResponse('saveError');
			}

			try {
				$quote->setCustomerMessage(Interspire_Request::post('customerMessage'));
				$quote->setStaffNotes(Interspire_Request::post('staffNotes'));

				$entity = new ISC_ENTITY_ORDER;

				$currency = GetDefaultCurrency();
				$order = array(
					'ordcurrencyid' => $currency['currencyid'],
					'ordcurrencyexchangerate' => $currency['currencyexchangerate'],
					'ordipaddress' => getIp(),
					'extraInfo' => array(),
					'quote' => $quote,
				);

				$createAccount = false;

				// process customer details to see if an account should be made
				if (Interspire_Request::post('orderFor') == 'new') {
					// this really needs to be split off into another method because it's done both at the front end checkout, in save billing, and in here! -ge
					$password = '';
					$confirmedPassword = '';
					$email = '';
					$accountFormFields = $GLOBALS['ISC_CLASS_FORM']->getFormFields(FORMFIELDS_FORM_ACCOUNT, true);
					$accountCustomFields = array();
					foreach($accountFormFields as $formFieldId => $formField) {
						$formFieldPrivateId = $formField->record['formfieldprivateid'];

						if (!$formFieldPrivateId) {
							$accountCustomFields[$formFieldId] = $formField->getValue();
						} else if($formFieldPrivateId == 'EmailAddress') {
							$email = $formField->getValue();
						} else if($formFieldPrivateId == 'Password') {
							$password = $formField->getValue();
						} else if($formFieldPrivateId == 'ConfirmPassword') {
							$confirmedPassword = $formField->getValue();
						}
					}

					// shouldn't reach this point with a valid email without all the details already being validated after step 1 > next, so go ahead and assign it to the order
					if ($email) {
						$createAccount = array(
							'addresses' => array(),
							'password' => $password,
							'customFormFields' => $accountCustomFields,
						);

						foreach ($quote->getAllAddresses() as /** @var ISC_QUOTE_ADDRESS */$address) {
							if (!$address->getSaveAddress()) {
								continue;
							}

							$customerAddress = $address->getAsArray();
							$customFields = $address->getCustomFields();
							if (!empty($customFields)) {
								$customerAddress['customFormFields'] = $customFields;

								// Shipping fields need to be mapped back to billing so they can be stored
								if ($address->getType() == ISC_QUOTE_ADDRESS::TYPE_SHIPPING) {
									$newCustomFields = array();
									$map = $GLOBALS['ISC_CLASS_FORM']->mapAddressFieldList(FORMFIELDS_FORM_SHIPPING, array_keys($customFields));
									foreach($map as $oldId => $newId) {
										$newCustomFields[$newId] = $customFields[$oldId];
									}
									$customerAddress['customFormFields'] = $newCustomFields;
								}
							}

							$createAccount['addresses'][] = $customerAddress;
						}
					}
				}

				if ($quote->getOrderId()) {
					$editing = true;
					$adding = false;

					$orderId = $quote->getOrderId();

					$existingOrder = $entity->get($orderId);
					if ($existingOrder['deleted']) {
						// don't allow saving changes for a deleted order
						$errors[] = GetLang('EditDeletedOrderError');
					} else {
						$order['orderid'] = $orderId;
						if (!$entity->edit($order)) {
							$errors[] = $entity->getError();
						}
					}
				} else {
					$editing = false;
					$adding = true;

					$order['orderpaymentmodule'] = '';

					$orderId = $entity->add($order);

					if ($orderId) {
						$quote->setOrderId($orderId);
					} else {
						$errors[] = $entity->getError();
					}
				}

				if (!empty($errors)) {
					$this->sendEditOrderResponse(array(
						'errors' => $errors,
						'stateTransition' => 'saveError',
					));
				}

				// retrieve the created/edited order record
				$order = GetOrder($orderId);

				if ($createAccount) {
					// this function doesn't return anything for error testing
					createOrderCustomerAccount($order, $createAccount);
				}

				// Process a payment
				$paymentMethod = Interspire_Request::post('paymentMethod');

				$providerSuccess = false;

				// Retrieve the payment method details
				$paymentFields = Interspire_Request::post('paymentField');
				if (!empty($paymentFields[$paymentMethod])) {
					$paymentFields = $paymentFields[$paymentMethod];
				}
				else {
					$paymentFields = array();
				}

				if ($quote->getGrandTotalWithStoreCredit() > 0 && ($adding || empty($order['ordpaymentstatus']) || empty($order['orderpaymentmodule'])) && !empty($paymentMethod)) {
					$gatewayAmount = $quote->getGrandTotalWithStoreCredit();

					$provider = null;

					// was a custom payment specified?
					if ($paymentMethod == 'custom') {
						$paymentMethodName = $paymentFields['custom_name'];
						$providerSuccess = true;
					}
					// actual payment module
					else {
						GetModuleById('checkout', $provider, $paymentMethod);
						if(is_object($provider)) {
							$paymentMethodName = $provider->GetDisplayName();

							if (method_exists($provider, 'ProcessManualPayment')) {
								// set the order token as required by various payment methods
								ISC_SetCookie('SHOP_ORDER_TOKEN', $order['ordtoken'], time() + (3600*24), true);
								// make the token immediately available
								$_COOKIE['SHOP_ORDER_TOKEN'] = $order['ordtoken'];

								// process the payment
								$result = $provider->ProcessManualPayment($order, $paymentFields);
								if ($result['result']) {
									$providerSuccess = true;
									$gatewayAmount = $result['amount'];

									FlashMessage(GetLang('OrderPaymentSuccess', array('amount' => FormatPrice($gatewayAmount), 'orderId' => $orderId, 'provider' => $paymentMethodName)), MSG_SUCCESS);
								}
								else {
									$errors[] = GetLang('OrderPaymentFail', array('orderId' => $orderId, 'provider' => $paymentMethodName, 'reason' => $result['message']));
								}
							}
							else {
								// all manual/offline methods will always be successfull
								$providerSuccess = true;
							}
						}
						else {
							// failed to get a payment module
						}
					}
				// if the grand total after minus the coupon,etc is 0 and it's adding order also the payment method is custom.
				} else if ($quote->getGrandTotalWithStoreCredit() == 0 && ($adding || empty($order['ordpaymentstatus']) || empty($order['orderpaymentmodule'])) && $paymentMethod == 'custom') {
					$paymentMethodName = $paymentFields['custom_name'];
					$providerSuccess = true;
				}

				// was payment successfull?
				if ($providerSuccess) {
					// record payment info for the order
					$updatedOrder = array(
						'orderpaymentmethod' 	=> $paymentMethodName,
						'orderpaymentmodule'	=> $paymentMethod,
					);

					$this->db->UpdateQuery("orders", $updatedOrder, "orderid = " . $orderId);

					// set appropriate status for the order
					if ($quote->isDigital()) {
						$newStatus = ORDER_STATUS_COMPLETED;
					}
					else {
						$newStatus = ORDER_STATUS_AWAITING_FULFILLMENT;
					}
					UpdateOrderStatus($orderId, $newStatus, false);

					// email invoice
					if (Interspire_Request::post('emailInvoiceToCustomer')) {
						EmailInvoiceToCustomer($orderId);
					}
				}

				if (!empty($errors)) {
					$response = array(
						'errors' => $errors,
						'stateTransition' => 'saveError',
					);
				}
				else {
					if ($editing) {
						FlashMessage(GetLang('OrderUpdated', array('orderId' => $orderId)), MSG_SUCCESS);
					} else {
						FlashMessage(GetLang('OrderCreated', array('orderId' => $orderId)), MSG_SUCCESS);
					}

					$response = array(
						'stateTransition' => 'saveOk',
					);

					// remove quote object from session after successful save and successful payment
					getClass('ISC_ADMIN_ORDERS')->deleteQuoteSession($quoteSession);
				}

				if ($adding) {
					$response['updateOrderId'] = $orderId;
				}

				$this->sendEditOrderResponse($response);
			} catch (ISC_QUOTE_EXCEPTION $exception) {
				$this->sendEditOrderResponse(array(
					'stateTransition' => 'saveError',
					'errors' => array(
						$exception->getMessage(),
					),
				));
			}
		}
Ejemplo n.º 2
0
/**
 * Completes a pending order and marks it's status as whatever it should be next.
 * This function will process any payments, capture amounts from gateways, increase
 * # sold for each product in the order, etc.
 *
 * @param string The pending order token.
 * @param int The status to set the completed order to.
 * @return boolean True if successful, false on failure.
 */
function CompletePendingOrder($pendingOrderToken, $status, $sendInvoice=true)
{
	$orderData = LoadPendingOrdersByToken($pendingOrderToken, true);
	if($orderData === false) {
		return false;
	}

	$processedStoreCredit = false;
	$processedGiftCertificates = false;
	$orderStoreCredit = 0;
	$orderTotalAmount = 0;

	// Flag used to create the customer record but only if atleast one order was successful
	$createCustomer = false;

	// Sum up our total amount and store credit
	foreach ($orderData['orders'] as $order) {
		if ($order['ordstatus'] != 0) {
			continue;
		}

		$orderStoreCredit += $order['ordstorecreditamount'];
		$orderTotalAmount += $order['total_inc_tax'];
	}

	// flag to indicate if we should send notifications? only if the order was previously incomplete and the new status isn't declined/cancelled/refunded
	$sendNotifications = false;

	foreach($orderData['orders'] as $order) {
		$newStatus = $status;

		// Wait, was the order already complete? Then we don't do anything
		if($order['ordstatus'] != ORDER_STATUS_INCOMPLETE) {
			continue;
		}

		// If this order is digital, and the status is awaiting fulfillment, there's nothing
		// to actually fulfill, so set it to completed.
		if($order['ordisdigital'] && $newStatus == ORDER_STATUS_AWAITING_FULFILLMENT) {
			$newStatus = ORDER_STATUS_COMPLETED;
		}

		$extraInfo = @unserialize($order['extrainfo']);
		if(!is_array($extraInfo)) {
			$extraInfo = array();
		}

		// only email and update order data (coupons, certificates, store credit etc) if it's not a declined, cancelled or refunded order
		if($newStatus != ORDER_STATUS_DECLINED && $newStatus != ORDER_STATUS_CANCELLED && $newStatus != ORDER_STATUS_REFUNDED) {
			$createCustomer = true;
			$sendNotifications = true;

			if($sendInvoice && !EmailInvoiceToCustomer($order['orderid'], $newStatus)) {
				$GLOBALS['HideError'] = "";
				$GLOBALS['ErrorMessage'] = GetLang('ErroSendingInvoiceEmail');
				$GLOBALS['HideSuccess'] = "none";
			}

			// Are we updating the inventory levels when an order has been placed?
			if(GetConfig('UpdateInventoryLevels') == 1) {
				DecreaseInventoryFromOrder($order['orderid']);
			}

			// If this order now complete, we need to activate any gift certificates
			if(OrderIsComplete($newStatus)) {
				$GLOBALS['ISC_CLASS_GIFTCERTIFICATES'] = GetClass('ISC_GIFTCERTIFICATES');
				$GLOBALS['ISC_CLASS_GIFTCERTIFICATES']->ActivateGiftCertificates($order['orderid']);
			}

			// If we've had one or more coupons been applied to this order, we now need to increment the number of uses
			$couponIds = array();
			$query = "
				SELECT *
				FROM [|PREFIX|]order_coupons
				WHERE ordcouporderid='".(int)$order['orderid']."'
			";
			$result = $GLOBALS['ISC_CLASS_DB']->Query($query);
			while($coupon = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) {
				$couponIds[] = $coupon['ordcouponid'];
			}
			if(!empty($couponIds)) {
				$couponsUsed = array_unique($couponIds);
				$couponList = implode(",", array_map("intval", $couponsUsed));
				$query = "
					UPDATE [|PREFIX|]coupons
					SET couponnumuses=couponnumuses+1
					WHERE couponid IN (".$couponList.")
				";
				$GLOBALS['ISC_CLASS_DB']->Query($query);

				foreach ($couponIds as $cid) {
					getclass('ISC_COUPON')->updatePerCustomerUsage($cid);
				}
			}

			// If we used store credit on this order, we now need to subtract it from the users account.
			if($order['ordstorecreditamount'] > 0 && $processedStoreCredit == false) {
				$GLOBALS['ISC_CLASS_CUSTOMER'] = GetClass('ISC_CUSTOMER');
				$currentCredit = $GLOBALS['ISC_CLASS_CUSTOMER']->GetCustomerStoreCredit($order['ordcustid']);
				$newCredit = $currentCredit - $orderStoreCredit;
				if($newCredit < 0) {
					$newCredit = 0;
				}
				$updatedCustomer = array(
					'custstorecredit' => $newCredit,
				);
				$GLOBALS['ISC_CLASS_DB']->UpdateQuery('customers', $updatedCustomer, "customerid='".(int)$order['ordcustid']."'");
				$processedStoreCredit = true;
			}

			// If one or more gift certificates were used we need to apply them to this order and subtract the total
			if($order['ordgiftcertificateamount'] > 0 && isset($extraInfo['giftcertificates']) && !empty($extraInfo['giftcertificates']) && $processedGiftCertificates == false) {
				$usedCertificates = array();
				$GLOBALS['ISC_CLASS_GIFT_CERTIFICATES'] = GetClass('ISC_GIFTCERTIFICATES');
				$GLOBALS['ISC_CLASS_GIFT_CERTIFICATES']->ApplyGiftCertificatesToOrder($order['orderid'], $orderTotalAmount + $order['ordgiftcertificateamount'], $extraInfo['giftcertificates'], $usedCertificates);
				unset($extraInfo['giftcertificates']);
				$processedGiftCertificates = true;
			}

			// If there are one or more digital products in this order then we need to create a record in the order_downloads table
			// for each of them and set the expiry dates
			$query = "
				SELECT ordprodid, ordprodqty
				FROM [|PREFIX|]order_products
				WHERE orderorderid='".$order['orderid']."' AND ordprodtype='digital'
			";
			$result = $GLOBALS['ISC_CLASS_DB']->Query($query);
			$digitalProductIds = array();
			while($digitalProduct = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) {
				$digitalProductIds[$digitalProduct['ordprodid']] = $digitalProduct;
			}

			if(!empty($digitalProductIds)) {
				$query = "
					SELECT downloadid, productid, downexpiresafter, downmaxdownloads
					FROM [|PREFIX|]product_downloads
					WHERE productid IN (".implode(',', array_keys($digitalProductIds)).")
				";
				$result = $GLOBALS['ISC_CLASS_DB']->Query($query);
				while($digitalDownload = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) {
					$expiryDate = 0;

					// If this download has an expiry date, set it to now + expiry time
					if($digitalDownload['downexpiresafter'] > 0) {
						$expiryDate = time() + $digitalDownload['downexpiresafter'];
					}

					// If they've purchased more than one, we need to give them max downloads X quantity downloads
					$quantity = $digitalProductIds[$digitalDownload['productid']]['ordprodqty'];

					$newDownload = array(
						'orderid' => $order['orderid'],
						'downloadid' => $digitalDownload['downloadid'],
						'numdownloads' => 0,
						'downloadexpires' => $expiryDate,
						'maxdownloads' => $digitalDownload['downmaxdownloads'] * $quantity
					);
					$GLOBALS['ISC_CLASS_DB']->InsertQuery('order_downloads', $newDownload);
				}
			}
		}

		// Does a customer account need to be created?
		if(!empty($extraInfo['createAccount'])) {
			createOrderCustomerAccount($order, $extraInfo['createAccount']);
			unset($extraInfo['createAccount']);
		}

		// Now update the order and set the status
		$updatedOrder = array(
			"ordstatus" => $newStatus,
			"extrainfo" => serialize($extraInfo)
		);
		$GLOBALS['ISC_CLASS_DB']->UpdateQuery("orders", $updatedOrder, "orderid='".$order['orderid']."'");
	}

	if($sendNotifications) {
		// Trigger all active new order notification methods
		SendOrderNotifications($pendingOrderToken);

		// Do we need to add them to a Interspire Email Marketer mailing list?
		SubscribeCustomerToLists($pendingOrderToken);

		// Update the current uses of each rule
		$quote = getCustomerQuote();
		$appliedRules = array_keys(getCustomerQuote()->getAppliedDiscountRules());
		if(!empty($appliedRules)) {
			require_once ISC_BASE_PATH.'/lib/rule.php';
			updateRuleUses($appliedRules);
		}
	}

	// Empty the users cart and kill the checkout process
	EmptyCartAndKillCheckout();

	return true;
}