|
|
- <?php
- if(!defined('QUINTACMS') ) die("No quinta.");
-
- if (!defined("CHECKOUTMODULE.CLASS.PHP")){
- define("CHECKOUTMODULE.CLASS.PHP",1);
-
- /**
- * Stage Types for the Checkout process - oh for an enum type ..
- * These stages represent each of the parts of the checkout process - they
- * are both set to keep track of which module is active so that we may take
- * appropriate actions on forward or back movements according to what the
- * previous Stage was and they are passed as ActionParameters to indicate
- * which Stage to show next.
- *
- * Note that ShoppingCart is not actually a stage module here - it is merely
- * a redirect to the ShoppingCartView page. Likewise, Cancel redirects to Home.
- *
- *@package Quinta
- *@subpackage Modules
- */
- class CheckOutStage{
- /**
- *@var constant integer - flags for indicating current stage of the process
- */
- const ShoppingCart = 0;
- const Shipping = 1;
- const Payment = 2;
- const Review = 3;
- const Confirmation = 4;
- const Cancel = 6;
- }
-
- /**
- * Class CheckOutModule - a module for the checkout process
- * This class provides a central page module with four modules that make
- * up a four part checkout process.
- *
- * The first part displays the shipping options information to the user with sections
- * for choosing addresses and shipping methods.
- *
- * The second part shows payment options and billing address.
- *
- * The third part shows the order again and all of the choices with
- * final charges and a confirmation action - the user may also choose to
- * return to a previous step to modify the quantities in the order or other selections
- * (addresses, shipping method, payment, etc.). Note that if they choose to modify
- * the quantity they are redirected to the ShoppingCartView page and must
- * reinitiate the checkout process (by clicking "Check Out").
- *
- * The final module confirms the status of the payment and displays misc confirmation
- * messages.
- *
- *@todo
- * - send order email confirmation
- * - PayPal Express checkout button and action on CheckOutEditModule panel, this
- * will basically call the paypal payment action, ie. it should behave like clicking the
- * "purchase" button in the PaymentModule ..
- *
- *@author Erik Winn <sidewalksoftware@gmail.com>
- *
- *@version 0.3
- *
- *@package Quinta
- * @subpackage Modules
- */
- class CheckOutModule extends QPanel{
- protected $objAccount = null;
- protected $objShoppingCart = null;
- protected $intCurrentStage = 0;
- /**
- *@var Order - the current order
- */
- protected $objOrder = null;
- /**
- *@var ShippingMethod - reference to the selected shipping method
- */
- protected $objShippingMethod = null;
- /**
- *@var PaymentMethod - reference to the selected payment method
- */
- protected $objPaymentMethod = null;
- /**
- *@var boolean blnCartEmpty - set to true if there are no items in the cart
- */
- protected $blnCartEmpty = true;
- /**
- * This indicates whether the customer has clicked "Purchase" yet - if they do this and
- * then hit back in the browser we want to clean up and start over ..
- * NOTE: this needs to be moved somewhere global to maintain state ( perhaps even $_SESSION since
- * the user may have been transferred offsite )
- *@todo - handle browser back actions when payment already initiated
- *@var boolean
- */
- protected $blnTransactionInitiated=false;
- /**
- *@var boolean blnTransactionApproved - indicates whether the payment was accepted or not
- */
- protected $blnTransactionApproved = false;
- /**
- *@var string strTransactionMessage - string to contain messages from the payment server
- */
- protected $strTransactionMessage;
- /**
- *@var string strErrors - general error storage ..
- */
- protected $strErrors;
- /**
- *@var Address - reference to the selected or default shipping address
- */
- public $objShippingAddress;
- /**
- *@var Address - reference to the selected or default billing address
- */
- public $objBillingAddress;
- /**
- * @var OrderTotalsController - view panel to display the order totals
- */
- public $objOrderTotalsController;
- /**
- * @var PaymentModule - module to manage the selection of a payment method
- */
- public $objPaymentModule;
- /**
- * @var ShippingModule - module to manage the selection of a shipping method
- */
- public $objShippingModule;
- /**
- * @var CheckOutReviewModule - module to display a review of the order and accept submission
- */
- public $objCheckOutReviewModule;
- /**
- * @var CheckOutConfirmationModule - module to display order/payment confirmation or errors
- */
- public $objCheckOutConfirmationModule;
- /**
- * @var Qlabel lblProgressBar - shows a progress image indicating the current stage
- */
- public $lblProgressBar;
- /**
- * This refers to the currently active module - it is assigned a module according to the current
- * stage of the process
- *@var QPanel - the current panel.
- */
- public $pnlCurrentPanel = null;
- /**
- * This panel shows the title heading for currently active module - it is assigned text according to the current
- * stage of the process
- *@var QPanel - the current panel title heading.
- */
- public $pnlHeading;
-
- public $btnContinue;
- public $btnCancel;
- public $btnBack;
-
- public $objWaitIcon;
- public $pnlHeading;
- public $lblMessages;
- /**
- * Module constructor
- * NOTE: This module ignores the required extra parameters ..
- *@param ContentBlockController - parent controller object.
- *@param mixed - extra parameters, ignored
- */
- public function __construct( ContentBlockController $objParentObject, $mixParameters=null){
-
- try {
- parent::__construct($objParentObject);
- } catch (QCallerException $objExc) {
- $objExc->IncrementOffset();
- throw $objExc;
- }
- $this->objAccount =& IndexPage::$objAccount;
- $this->objShoppingCart =& IndexPage::$objShoppingCart;
- $this->AutoRenderChildren = true;
- $this->strTemplate = __QUINTA_CORE_VIEWS__ . '/CheckOutModule.tpl.php';
-
- $this->pnlHeading = new QPanel($this);
- $this->pnlHeading->HtmlEntities = false;
- $this->pnlHeading->AddCssClass('CheckoutHeading');
-
- $this->lblProgressBar = new QLabel($this);
- $this->lblProgressBar->HtmlEntities = false;
- $this->lblProgressBar->CssClass = 'ProgressBarShipping';
- $this->lblProgressBar->Text = sprintf('<span class="heading">%s</span><span class="label">%s</span>
- <span class="label">%s</span><span class="label">%s</span>
- <span class="label">%s</span><span class="label">%s</span>',
- STORE_NAME . ' ' . Quinta::Translate('Checkout Process') . ':',
- Quinta::Translate('cart'),
- Quinta::Translate('shipping'),
- Quinta::Translate('payment'),
- Quinta::Translate('review order'),
- Quinta::Translate('receipt'));
-
- $this->lblMessages = new QLabel($this);
- $this->lblMessages->HtmlEntities = false;
-
- $this->pnlCurrentPanel = new QPanel($this);
- $this->pnlCurrentPanel->AutoRenderChildren = true;
-
- // Only show anything if there is an active Account and ShoppingCart
- if( $this->objAccount instanceof Account && $this->objShoppingCart instanceof ShoppingCart){
- $this->objOrder = $this->objShoppingCart->CreateNewOrder(true);
- $aryOrderItems = $this->objOrder->GetNewOrderItemsArray();
-
- if ( empty($aryOrderItems) ){
- $this->pnlHeading->Text = ' ' . Quinta::Translate('Empty Cart');
- // $this->pnlCurrentPanel = new QPanel($this);
- $this->pnlCurrentPanel->Text = Quinta::Translate('There are no items to check out') . '.';
- return;
- }
- else
- $this->blnCartEmpty = false;
-
- //create a display of the order summary ..
- $this->objOrderTotalsController = new OrderTotalsController($this, $this->objOrder);
-
- //create buttons to manage work flow between panels - the signals are handled here.
- // Continue goes to the next panel, Back returns to previous panel, Cancel goes Home
- $this->btnContinue = new QButton($this);
- $this->btnContinue->Text = Quinta::Translate('Continue');
- if(IndexPage::$blnAjaxOk)
- $this->btnContinue->AddAction(new QClickEvent(), new QAjaxControlAction($this, 'btnContinue_Click'));
- else
- $this->btnContinue->AddAction(new QClickEvent(), new QServerControlAction($this, 'btnContinue_Click'));
-
- $this->btnBack = new QButton($this);
- $this->btnBack->Text = Quinta::Translate('Back');
- if(IndexPage::$blnAjaxOk)
- $this->btnBack->AddAction(new QClickEvent(), new QAjaxControlAction($this, 'btnBack_Click'));
- else
- $this->btnBack->AddAction(new QClickEvent(), new QServerControlAction($this, 'btnBack_Click'));
-
- $this->btnCancel = new QButton($this);
- $this->btnCancel->Text = Quinta::Translate('Cancel');
- if(IndexPage::$blnAjaxOk)
- $this->btnCancel->AddAction(new QClickEvent(), new QAjaxControlAction($this, 'btnCancel_Click'));
- else
- $this->btnCancel->AddAction(new QClickEvent(), new QServerControlAction($this, 'btnCancel_Click'));
-
- $this->objWaitIcon = new QWaitIcon($this);
- //start out with the shipping options displayed
- $this->GoForward( CheckOutStage::Shipping );
- }
- else
- $this->pnlCurrentPanel->Text = Quinta::Translate('We are sorry, you must be logged in to check out.');
- }
- /**
- * This function is called when the user clicks "Back" - it checks for the current stage and
- * calls GoBack with the appropriate parameters.
- *@param string strFormId - a string representation of the CSS Id for the main form
- *@param string strControlId - a string representation of the CSS Id for the control calling this function
- *@param string strParameter - a string containing optionally set parameters
- */
- public function btnBack_Click($strFormId, $strControlId, $strParameter){
- switch( $this->intCurrentStage ){
- case CheckOutStage::Shipping:
- return $this->GoBack(CheckOutStage::ShoppingCart);
- case CheckOutStage::Payment:
- return $this->GoBack(CheckOutStage::Shipping);
- case CheckOutStage::Review:
- return $this->GoBack(CheckOutStage::Payment);
- }
- }
- /**
- * This function is called when the user clicks "Continue" - it checks for the current stage and
- * calls GoForward with the appropriate parameters.
- *@param string strFormId - a string representation of the CSS Id for the main form
- *@param string strControlId - a string representation of the CSS Id for the control calling this function
- *@param string strParameter - a string containing optionally set parameters
- */
- public function btnContinue_Click($strFormId, $strControlId, $strParameter){
- switch( $this->intCurrentStage ){
- case CheckOutStage::Shipping:
- return $this->GoForward(CheckOutStage::Payment);
- case CheckOutStage::Payment:
- return $this->GoForward(CheckOutStage::Review);
- case CheckOutStage::Review:
- return $this->GoForward(CheckOutStage::Confirmation);
- case CheckOutStage::Confirmation:
- QApplication::Redirect(__QUINTA_SUBDIRECTORY__ . '/index.php/Home');
- }
- }
- /**
- * This function is called when the user clicks "Cancel" - it deletes the order and redirects
- * the user to the home page ..
- *@param string strFormId - a string representation of the CSS Id for the main form
- *@param string strControlId - a string representation of the CSS Id for the control calling this function
- *@param string strParameter - a string containing optionally set parameters
- */
- public function btnCancel_Click($strFormId, $strControlId, $strParameter){
- //NOTE: perhaps we should clear the shopping cart here? or provide some
- // other way to do that somewhere ..
- $this->GoForward(CheckOutStage::Cancel);
- }
-
- public function GoBack($NextStage){
- $this->pnlCurrentPanel->RemoveChildControls(false);
-
- switch($NextStage){
- case CheckOutStage::ShoppingCart:
- Quinta::Redirect('http://' . Quinta::$ServerName . __QUINTA_SUBDIRECTORY__ . '/index.php/ShoppingCart');
- break;
- case CheckOutStage::Shipping:
- $this->pnlHeading->Text = ' ' . Quinta::Translate('Shipping Options');
- $this->lblProgressBar->CssClass = 'ProgressBarShipping';
- $this->objShippingModule->SetParentControl($this->pnlCurrentPanel);
- $this->objOrderTotalsController->Visible = true;
- break;
- case CheckOutStage::Payment:
- $this->pnlHeading->Text = ' ' . Quinta::Translate('Payment Options');
- $this->lblProgressBar->CssClass = 'ProgressBarPayment';
- $this->objPaymentModule->SetParentControl($this->pnlCurrentPanel);
- /* if($this->objCheckOutReviewModule instanceof CheckOutReviewModule)
- $this->objCheckOutReviewModule->Visible = false;*/
- $this->objOrderTotalsController->Visible = true;
- break;
- }
-
- $this->btnContinue->Text = Quinta::Translate('Continue');
- $this->intCurrentStage = $NextStage;
- }
-
- private function GoForward($NextStage){
- if(null !== $this->pnlCurrentPanel){
- $this->pnlCurrentPanel->Visible = false;
- $this->pnlCurrentPanel->RemoveChildControls(false);
- }
- switch($NextStage){
- case CheckOutStage::Shipping:
- //the spaces make room for the CSS cart image ..
- $this->pnlHeading->Text = ' ' . Quinta::Translate('Shipping Options');
- $this->lblProgressBar->CssClass = 'ProgressBarShipping';
-
- //to debug: $this->objShippingModule = new ShippingModule($this->pnlCurrentPanel, $this, $this->objOrder, true);
- $this->objShippingModule = new ShippingModule($this->pnlCurrentPanel, $this, $this->objOrder);
- $this->intCurrentStage = CheckOutStage::Shipping;
- break;
- case CheckOutStage::Payment:
- $this->pnlHeading->Text = ' ' . Quinta::Translate('Payment Options');
- $this->lblProgressBar->CssClass = 'ProgressBarPayment';
- if($this->objPaymentModule instanceof PaymentModule)
- $this->objPaymentModule->SetParentControl($this->pnlCurrentPanel);
- else{
- $this->pnlCurrentPanel->RemoveChildControls(false);
- $this->objPaymentModule = new PaymentModule($this->pnlCurrentPanel, $this->objOrder);
- }
- $this->intCurrentStage = CheckOutStage::Payment;
- break;
- case CheckOutStage::Review:
- $this->pnlHeading->Text = ' ' . Quinta::Translate('Order Review');
- $this->lblProgressBar->CssClass = 'ProgressBarReview';
- if($this->objCheckOutReviewModule instanceof CheckOutReviewModule){
- $this->objCheckOutReviewModule->RefreshView($this->objOrder);
- $this->objCheckOutReviewModule->SetParentControl($this->pnlCurrentPanel);
- }else{
- $this->pnlCurrentPanel->RemoveChildControls(true);
- $this->objCheckOutReviewModule = new CheckOutReviewModule($this->pnlCurrentPanel, $this, $this->objOrder);
- }
- //the review panel draws its own order totals
- $this->objOrderTotalsController->Visible = false;
- //We have to do a server post back to allow the header(Location:) redirect for PayPal to work
- // in all cases, else the browser just gets it in XML and does nothing (some complain in fact) ..
- $this->btnContinue->RemoveAllActions(QClickEvent::EventName);
- $this->btnContinue->AddAction(new QClickEvent(), new QServerControlAction($this, 'btnContinue_Click'));
- $this->btnContinue->Text = Quinta::Translate('Submit');
-
- $this->intCurrentStage = CheckOutStage::Review;
- break;
- case CheckOutStage::Confirmation:
- $this->pnlHeading->Text = ' ' . Quinta::Translate('Your order has been received') . '.';
- $this->lblProgressBar->CssClass = 'ProgressBarReceipt';
- $this->pnlCurrentPanel->RemoveChildControls(true);
-
- //submit payment ..
- $this->SubmitPayment();
- $this->btnBack->Visible = false;
- $this->btnCancel->Visible = false;
- $this->btnContinue->Text = Quinta::Translate('Continue');
- // set up the confirmation panel ..
- $this->ShowCheckOutConfirmationModule();
-
- $this->intCurrentStage = CheckOutStage::Confirmation;
- break;
- case CheckOutStage::ShoppingCart:
- Quinta::Redirect('http://' . Quinta::$ServerName . __QUINTA_SUBDIRECTORY__ . '/index.php/ShoppingCart');
- break;
- case CheckOutStage::Cancel:
- //this shouldn't happen (since we don't save til the end) but maybe:
- if($this->objOrder instanceof Order && null != $this->objOrder->Id)
- $this->objOrder->Delete();
- QApplication::Redirect(__QUINTA_SUBDIRECTORY__ . '/index.php/Home');
- break;
- default:
- throw new QCallerException("CheckOutModule: Unknown Stage passed.");
- }
- $this->pnlCurrentPanel->Visible = true;
- }
-
- public function RefreshOrderTotalsController(){
- $this->objOrderTotalsController->SetTotals($this->objOrder);
- }
- /**
- * The final act - this function recieves the signal to process payment for an Order. This
- * is where a PaymentAction for the order is created and triggered.
- * Here we also first save the order and update the order status, order_status_history - initially
- * to "Pending" and to "Paid" on approval. Note that if a PaymentAction fails the action itself
- * deletes the order so no further action is taken here in that case.
- *
- * NOTE: Payment methods that require redirecting the user away from here (eg. PayPal ) or
- * methods that do not accept immediate payment (Pay by Mail, Fax, wire transfer etc ..)
- * must naturally finish the process elsewhere so status will remain "Pending" for these.
- */
- public function SubmitPayment(){
- ///@todo Fixme - this doesn't work if the user hits back in browser, figure out a check here ..
- if( $this->blnTransactionInitiated )
- throw new QCallerException('Error: attempt to submit payment already initiated.');
- if( $this->objOrder->Id )
- throw new QCallerException('Error: attempt to submit payment on completed order.');
-
- $this->blnTransactionInitiated = true;
-
- $this->objOrder->StatusId = OrderStatusType::Pending;
- $this->objOrder->Notes = $this->objShippingModule->Notes;
- //save the order and get an ID (reload is automatic)..
- $intOrderId = $this->objOrder->Save();
- //make an entry into the order_status_history table
- $objOrderStatusHistory = new OrderStatusHistory();
- $objOrderStatusHistory->StatusId = OrderStatusType::Pending;
- $objOrderStatusHistory->OrderId = $intOrderId;
- $objOrderStatusHistory->Notes = $this->objShippingModule->Notes;
- $objOrderStatusHistory->Save();
-
- //save the order items - must be done after the order id is set ..
- $this->objOrder->SaveNewOrderItems();
-
- //create a payment action object ..
- $strActionClass = $this->objPaymentModule->SelectedMethod->ActionClassName;
- if(!class_exists($strActionClass))
- throw new QCallerException( 'Missing PaymentMethod Class: ' . $strActionClass );
-
- $objPaymentAction = new $strActionClass($this->objOrder);
-
- //retrieve credit card input and initialize payment action
- if($this->objPaymentModule->SelectedMethod->RequiresCcNumber){
- $objPaymentAction->CCNumber = $this->objPaymentModule->txtCCNumber->Text;
- $objPaymentAction->CCExpirationYear = $this->objPaymentModule->lstCCExpirationYear->SelectedValue;
- $objPaymentAction->CCExpirationMonth = $this->objPaymentModule->lstCCExpirationMonth->SelectedValue;
- $objPaymentAction->CCVNumber = $this->objPaymentModule->txtCCVNumber->Text;
- }
-
- $objPaymentAction->TestMode = $this->objPaymentModule->SelectedMethod->TestMode;
-
- //Note: at this point PayPal Express will redirect to paypal.com ..
- $objPaymentAction->PreProcess();
- $objPaymentAction->Process();
- $objPaymentAction->PostProcess();
-
- //set some data for the confirmation module ..
- $this->strTransactionMessage = $objPaymentAction->StatusText;
- $this->strErrors = $objPaymentAction->Errors;
- $this->blnTransactionApproved = $objPaymentAction->Approved;
- }
- /**
- * This function is called when a purchase has been completed.
- * Basically it hides all the other panels/modules and shows the payment confirmation message.
- * Any changes to the confirmation display should be done here.
- */
- public function ShowCheckOutConfirmationModule(){
- $this->objCheckOutConfirmationModule = new CheckOutConfirmationModule($this->pnlCurrentPanel, $this, $this->objOrder);
-
- $strApprovedText = '<div class="heading">' . Quinta::Translate("Thank You for your purchase") . '! </div>';
- $strApprovedText .= '</p>' . Quinta::Translate('Your Order Number is') . ': ' . $this->objOrder->Id . ' </p>';
-
- $strApprovedText .= '</p>' . Quinta::Translate('We will email you shortly with a confirmation of your order') . '. </p>'
- . '</p>' . Quinta::Translate('Please make sure that you have given a correct email address with which to contact you') . '. </p>';
-
- $strDeclinedText = Quinta::Translate(" We're Sorry - Your payment has been declined - the message below may help to solve the problem:") . '. <br />';
-
- if( $this->blnTransactionApproved )
- $this->objCheckOutConfirmationModule->Message = $strApprovedText;
- else{
- //for testing - extra error messages ..
- // $msg = $strDeclinedText . '<p> Transaction Errors: <br />' . $this->strErrors . '</p>' ;
- // $this->objCheckOutConfirmationModule->Message = $msg ;
- $this->objCheckOutConfirmationModule->Message = $strDeclinedText ;
- }
- if('' != $this->strTransactionMessage)
- $this->objCheckOutConfirmationModule->Message .= '<p> Transaction Messages: <br />' . $this->strTransactionMessage . '</p>' ;
- $this->objCheckOutConfirmationModule->Visible = true;
- }
-
- public function Validate(){return true;}
- ///Gettors
- public function __get($strName){
- switch ($strName){
- case 'Errors':
- return $this->strErrors ;
- case 'Order':
- return $this->objOrder ;
- case 'ShippingAddress':
- return $this->objShippingModule->Address;
- case 'BillingAddress':
- return $this->objPaymentModule->Address;
- case 'ShippingMethod':
- return $this->objShippingModule->SelectedMethod;
- case 'PaymentMethod':
- return $this->objPaymentModule->SelectedMethod;
- case 'ShippingModule':
- return $this->objShippingModule;
- case 'TotalOunces':
- return $this->objOrder->fltTotalOunces;
- case 'TotalPounds':
- return $this->objOrder->intTotalPounds;
- case 'XAxisSize':
- return $this->objOrder->fltXAxisSize;
- case 'YAxisSize':
- return $this->objOrder->fltYAxisSize;
- case 'ZAxisSize':
- return $this->objOrder->fltZAxisSize;
- case 'CartEmpty':
- return $this->blnCartEmpty;
- default:
- try {
- return parent::__get($strName);
- } catch (QCallerException $objExc) {
- $objExc->IncrementOffset();
- throw $objExc;
- }
- }
- }
- ///Settors
- public function __set($strName, $mixValue){
- switch ($strName){
- case 'Order':
- try {
- return ($this->objOrder = QType::Cast($mixValue, 'Order' ));
- } catch (QInvalidCastException $objExc) {
- $objExc->IncrementOffset();
- throw $objExc;
- }
-
- default:
- try {
- return (parent::__set($strName, $mixValue));
- } catch (QCallerException $objExc) {
- $objExc->IncrementOffset();
- throw $objExc;
- }
- }
- }
-
- ////////////// temp .. ////////////////////////////////////////////////////////////////
-
- /**
- * This function refreshes the information in the order to match user input
- *@todo this should update the CheckOutReviewModule.
- */
- /* private function refreshOrderItems()
- {
- // $this->lblMessages->Text='';
-
- //rebuild Order items array according to user input (quantities)
- $this->aryOrderItems = array();
- $aryItemViews =& $this->objCheckOutEditModule->ItemListModule->aryCheckOutItemControllers;
- foreach( $aryItemViews as $objView )
- {
- $item = $objView->objOrderItem;
- //objView->Quantity is from the input field
- $item->Quantity = $objView->Quantity;
- $this->aryOrderItems[] = $item;
- }
- //adjust shopping cart to match.
- $aryCartItems =$this->objShoppingCart->GetShoppingCartItemArray();
- foreach($aryCartItems as &$objCartItem )
- {
- foreach($this->aryOrderItems as $oi_index => $objOrderItem )
- {
- if($objOrderItem->ProductId != $objCartItem->ProductId)
- continue;
- //if the quantity was set to zero or less, remove from both arrays .
- if($objOrderItem->Quantity <= 0)
- {
- $objCartItem->Delete();
- unset($this->aryOrderItems[$oi_index]);
- }
- else
- {
- $objCartItem->Quantity = $objOrderItem->Quantity;
- $objCartItem->Save();
- }
- }
- }
- //refresh the shopping cart's idea of its contents ..
- $this->objShoppingCart->Reload();
-
- }*/
-
- }//end class
- }//end define
- ?>
|