|
<?php
|
|
if(!defined('QUINTACMS') ) die("No quinta.");
|
|
|
|
if (!defined("SHIPPINGMODULE.CLASS.PHP")){
|
|
define("SHIPPINGMODULE.CLASS.PHP",1);
|
|
|
|
/**
|
|
* Class ShippingModule - a module to display a selection of ShippingMethods
|
|
*
|
|
* This class obtains any shipping methods flagged as active in the database and
|
|
* adds them to a radiobutton list which is the only display object. For each method
|
|
* a new ShippingCalculator is instantiated and GetEstimate is called to obtain a price
|
|
* for the order based on information in the OrderItems and the addresses (ie. weight,
|
|
* size and destination).
|
|
*
|
|
* @todo
|
|
* - check availability more gracefully
|
|
* - handle errors
|
|
*
|
|
*@author Erik Winn <sidewalksoftware@gmail.com>
|
|
*
|
|
*@version 0.3
|
|
*
|
|
*@package Quinta
|
|
* @subpackage Modules
|
|
*/
|
|
class ShippingModule extends QPanel{
|
|
public $blnDebug = false;
|
|
/**
|
|
* This is the main control block for this module - it is designed to be the CheckOutModule,
|
|
* but it may be used with other controllers as long as you pass an Order object as we obtain
|
|
* address information, weight, etc from the order ..
|
|
*@var QPanel objControlBlock - the main control block for this module, usually CheckOutModule
|
|
*/
|
|
protected $objControlBlock;
|
|
/**
|
|
*@var Order objOrder - a local reference to the Order
|
|
*/
|
|
protected $objOrder;
|
|
/**
|
|
*@var Address objAddress - a local reference to the Address
|
|
*/
|
|
protected $objAddress;
|
|
/**
|
|
*@var ShippingMethod objSelectedMethod - the selected method for this module
|
|
*/
|
|
protected $objSelectedMethod;
|
|
/**
|
|
*@var array aryShippingMethods - the active methods available for this module
|
|
*/
|
|
protected $aryShippingMethods;
|
|
|
|
protected $strDefaultCarrier;
|
|
protected $strDefaultServiceType;
|
|
|
|
protected $fltShippingTotal;
|
|
protected $blnHasActiveMethods=true;
|
|
protected $blnIsInternational=false;
|
|
protected $intPreviousAddressId;
|
|
/**
|
|
* This is a mapping of shipping provider to method. Each ShippingMethod has a title
|
|
* field, this is displayed at the top of a block showing each method active for a provider.
|
|
* The title field is used as the title of the block, so the map is in the form title => objShippingMethodController
|
|
* the view containing a radiobutton for the selection of that method.
|
|
*@var array aryShippingProviders - an array/map of Carriers to methods
|
|
*/
|
|
public $aryShippingProviders;
|
|
|
|
/**
|
|
*@var AddressSelectionModule objAddressSelectionModule - handles selecting the shipping address
|
|
*/
|
|
public $objAddressSelectionModule;
|
|
/**
|
|
*@var QTextBox txtNotes - customer comments added to order ..
|
|
*/
|
|
public $txtNotes;
|
|
/**
|
|
* Module constructor
|
|
*@param QPanel objControlBlock - the main control block for this module, usually CheckOutModule
|
|
*@param Order objOrder - the order to be shipped ...
|
|
*/
|
|
public function __construct($pnlParentObject, $objControlBlock, Order $objOrder, $blnDebug = false ){
|
|
|
|
try {
|
|
parent::__construct($pnlParentObject );
|
|
} catch (QCallerException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
|
|
if($blnDebug)
|
|
$this->blnDebug = $blnDebug;
|
|
$this->strDefaultCarrier = DEFAULT_SHIPPING_CARRIER;
|
|
$this->strDefaultServiceType = DEFAULT_SHIPPING_SERVICE;
|
|
|
|
//normally refers to the CheckOutModule ..
|
|
$this->objControlBlock =& $objControlBlock;
|
|
|
|
$this->objOrder =& $objOrder;
|
|
|
|
$this->AutoRenderChildren = true;
|
|
$this->strTemplate = __QUINTA_CORE_VIEWS__ . '/ShippingModule.tpl.php';
|
|
|
|
$this->initMethodViews();
|
|
if( !empty($this->aryShippingMethods)){
|
|
$this->objAddressSelectionModule = new AddressSelectionModule($this,
|
|
'SelectAddress',
|
|
$this->objOrder->ShippingAddressId
|
|
);
|
|
$this->objAddress = $this->objAddressSelectionModule->Address;
|
|
}else
|
|
$this->blnHasActiveMethods = false;
|
|
|
|
$this->txtNotes = new QTextBox($this);
|
|
$this->txtNotes->TextMode = QTextMode::MultiLine;
|
|
$this->txtNotes->Columns = 30;
|
|
}
|
|
|
|
/**
|
|
* This function initializes the array of potential shipping methods
|
|
*@todo make the local pickup option configurable, currently you have to change the check here ..
|
|
*/
|
|
protected function initShippingMethods(){
|
|
if( $this->objOrder->IsInternational)
|
|
$this->aryShippingMethods = ShippingMethod::QueryArray( QQ::AndCondition(
|
|
QQ::Equal(QQN::ShippingMethod()->Active, true),
|
|
QQ::Equal(QQN::ShippingMethod()->IsInternational,true)
|
|
));
|
|
else
|
|
$this->aryShippingMethods = ShippingMethod::QueryArray(
|
|
QQ::Equal(QQN::ShippingMethod()->Active, true)
|
|
);
|
|
|
|
if(ZoneType::Colorado == $this->objOrder->ShippingZoneId )
|
|
$this->aryShippingMethods[] = ShippingMethod::Load(1);
|
|
}
|
|
/**
|
|
* This function creates a radio button to display for each active shipping method as
|
|
* appropriate - if the method is not international no button will be created for an international
|
|
* order and if a method is not available or returns a 0 rate charge it will also not be created.
|
|
* @todo
|
|
* - check availability
|
|
* - implement try/catch to handle errors, log them when not debugging.
|
|
*/
|
|
protected function initMethodViews(){
|
|
$this->initShippingMethods();
|
|
|
|
if( empty($this->aryShippingMethods))
|
|
return;
|
|
|
|
if( is_array($this->aryShippingProviders) ){
|
|
foreach( $this->aryShippingProviders as $strName => &$aryMethodViews ){
|
|
foreach($aryMethodViews as $it => $objMethodView){
|
|
$strControlId = $objMethodView->ControlId;
|
|
$this->RemoveChildControl($strControlId, true);
|
|
unset($aryMethodViews[$it]);
|
|
}
|
|
unset($this->aryShippingProviders[$strName]);
|
|
}
|
|
$this->aryShippingProviders = array();
|
|
}
|
|
|
|
foreach($this->aryShippingMethods as $objShippingMethod){
|
|
//Fedex Ground international only goes for Canada
|
|
if($this->objOrder->IsInternational && CountryType::Canada != $this->objOrder->ShippingCountryId
|
|
&& false !== stripos( $objShippingMethod->ServiceType, 'FEDEX_GROUND' ) )
|
|
continue;
|
|
//Skip the Fedex international methods for domestic orders ..
|
|
if( !$this->objOrder->IsInternational
|
|
&& ( false !== stripos( $objShippingMethod->ServiceType, 'GLOBAL' )
|
|
|| false !== stripos( $objShippingMethod->ServiceType, 'INTERNATIONAL' )) )
|
|
continue;
|
|
|
|
$objShippingMethod->Init($this->objOrder);
|
|
///@todo check availability ..
|
|
/* if( ! $objShippingMethod->MethodAvailable() ) continue;*/
|
|
$objShippingMethod->GetRate();
|
|
/**
|
|
*@todo figure this out - USPS, eg. provides no clear way to determine availability (in fact i can't even
|
|
* find their !@#$ing error codes ..) so for now if there is no charge we assume it is not available ..
|
|
*/
|
|
if( ! $objShippingMethod->IsAvailable || $objShippingMethod->HasErrors || 0 == $objShippingMethod->Rate ){
|
|
// if($this->blnDebug && 'FEDEX_2_DAY' != $objShippingMethod->ServiceType)
|
|
if($this->blnDebug)
|
|
// exit(var_dump($objShippingMethod));
|
|
die($objShippingMethod->Title . ', '
|
|
. $objShippingMethod->ServiceType . '<br /> '
|
|
. $objShippingMethod->Errors );
|
|
else
|
|
continue;
|
|
}
|
|
//eh, could be a server error .. skip it. todo: make me smarter ..
|
|
if( ! is_numeric($objShippingMethod->Rate) )
|
|
continue;
|
|
|
|
$objShippingMethodController = new ShippingMethodController($this, $objShippingMethod);
|
|
|
|
//set the defaults here - note that if the default method is not active this leaves everything
|
|
//null until/unless the user selects a method; hence default should be properly configured.
|
|
if( $objShippingMethod->Carrier == $this->strDefaultCarrier
|
|
&& $objShippingMethod->ServiceType == $this->strDefaultServiceType )
|
|
{
|
|
$objShippingMethodController->Checked = true;
|
|
$this->objSelectedMethod = $objShippingMethod;
|
|
$this->objOrder->ShippingMethodId = $objShippingMethod->Id;
|
|
$this->objOrder->ShippingCharged = $objShippingMethod->Rate;
|
|
}
|
|
//store by title for the method display ..
|
|
$this->aryShippingProviders[$objShippingMethod->Title][] = $objShippingMethodController;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This Function is called when the user selects a method - it sets objSelectedMethod
|
|
* and updates ShippingMethodId and ShippingCharged in the Order ..
|
|
*@param integer intShippingMethodId - the id of the selected method
|
|
*/
|
|
public function SelectMethod($intShippingMethodId){
|
|
foreach($this->aryShippingMethods as $objMethod )
|
|
if($intShippingMethodId == $objMethod->Id){
|
|
//this is redundant .. todo: pick one way or the other?
|
|
$this->objSelectedMethod = $objMethod;
|
|
$this->objOrder->ShippingMethodId = $objMethod->Id;
|
|
$this->objOrder->ShippingCharged = $objMethod->Rate;
|
|
if($this->objControlBlock instanceof CheckOutModule)
|
|
$this->objControlBlock->RefreshOrderTotalsController();
|
|
break;
|
|
}
|
|
}
|
|
|
|
public function SelectAddress($intAddressId, $strParameter=null){
|
|
if( is_numeric($intAddressId) ){
|
|
$this->intPreviousAddressId = $intAddressId;
|
|
$this->objOrder->SetShippingAddress($this->objAddressSelectionModule->Address);
|
|
$this->objAddress = $this->objAddressSelectionModule->Address;
|
|
}
|
|
|
|
$this->objAddressSelectionModule->RemoveChildControls(true);
|
|
$this->RemoveChildControl($this->objAddressSelectionModule->ControlId, false);
|
|
|
|
if( 'Edit' == $strParameter )
|
|
$this->objAddressSelectionModule = new AddressSelectionModule($this, 'SelectAddress', $intAddressId, true);
|
|
elseif( 'New' == $strParameter )
|
|
$this->objAddressSelectionModule = new AddressSelectionModule($this, 'SelectAddress', null, true);
|
|
else{//Note: includes Save and Cancel .
|
|
if($intAddressId)
|
|
$this->objAddressSelectionModule = new AddressSelectionModule($this, 'SelectAddress', $intAddressId);
|
|
else
|
|
$this->objAddressSelectionModule = new AddressSelectionModule($this, 'SelectAddress', $this->intPreviousAddressId);
|
|
//Refresh the options listing ..
|
|
$this->initMethodViews();
|
|
}
|
|
|
|
$this->objAddressSelectionModule->Visible = true;
|
|
$this->AddChildControl($this->objAddressSelectionModule);
|
|
}
|
|
/**
|
|
* This Function is called when any input is sent - on failure the
|
|
* fields are redrawn with optional error messages.
|
|
*/
|
|
public function Validate(){
|
|
$blnToReturn = true;
|
|
// validate input here
|
|
return $blnToReturn;
|
|
}
|
|
|
|
public function __get($strName){
|
|
switch ($strName){
|
|
case 'Notes':
|
|
return $this->txtNotes->Text ;
|
|
case 'SelectedMethod':
|
|
return $this->objSelectedMethod ;
|
|
case 'Address':
|
|
return $this->objAddress ;
|
|
case 'HasActiveMethods':
|
|
return $this->blnHasActiveMethods ;
|
|
case 'IsInternational':
|
|
return $this->blnIsInternational ;
|
|
case 'DefaultCarrier':
|
|
return $this->strDefaultCarrier ;
|
|
case 'DefaultServiceType':
|
|
return $this->strDefaultServiceType ;
|
|
default:
|
|
try {
|
|
return parent::__get($strName);
|
|
} catch (QCallerException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
}
|
|
}
|
|
public function __set($strName, $mixValue){
|
|
switch ($strName){
|
|
case 'IsInternational':
|
|
try {
|
|
return ($this->blnIsInternational = QType::Cast($mixValue, Qtype::Boolean ));
|
|
} catch (QInvalidCastException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
case 'DefaultCarrier':
|
|
try {
|
|
return ($this->strDefaultCarrier = QType::Cast($mixValue, Qtype::String ));
|
|
} catch (QInvalidCastException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
case 'DefaultServiceType':
|
|
try {
|
|
return ($this->strDefaultServiceType = QType::Cast($mixValue, Qtype::String ));
|
|
} catch (QInvalidCastException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
|
|
default:
|
|
try {
|
|
return (parent::__set($strName, $mixValue));
|
|
} catch (QCallerException $objExc) {
|
|
$objExc->IncrementOffset();
|
|
throw $objExc;
|
|
}
|
|
}
|
|
}
|
|
|
|
}//end class
|
|
}//end define
|
|
?>
|