A QCodo powered CMS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

327 lines
12 KiB

  1. <?php
  2. if(!defined('QUINTACMS') ) die("No quinta.");
  3. if (!defined("SHIPPINGMODULE.CLASS.PHP")){
  4. define("SHIPPINGMODULE.CLASS.PHP",1);
  5. /**
  6. * Class ShippingModule - a module to display a selection of ShippingMethods
  7. *
  8. * This class obtains any shipping methods flagged as active in the database and
  9. * adds them to a radiobutton list which is the only display object. For each method
  10. * a new ShippingCalculator is instantiated and GetEstimate is called to obtain a price
  11. * for the order based on information in the OrderItems and the addresses (ie. weight,
  12. * size and destination).
  13. *
  14. * @todo
  15. * - check availability more gracefully
  16. * - handle errors
  17. *
  18. *@author Erik Winn <sidewalksoftware@gmail.com>
  19. *
  20. *@version 0.3
  21. *
  22. *@package Quinta
  23. * @subpackage Modules
  24. */
  25. class ShippingModule extends QPanel{
  26. public $blnDebug = false;
  27. /**
  28. * This is the main control block for this module - it is designed to be the CheckOutModule,
  29. * but it may be used with other controllers as long as you pass an Order object as we obtain
  30. * address information, weight, etc from the order ..
  31. *@var QPanel objControlBlock - the main control block for this module, usually CheckOutModule
  32. */
  33. protected $objControlBlock;
  34. /**
  35. *@var Order objOrder - a local reference to the Order
  36. */
  37. protected $objOrder;
  38. /**
  39. *@var Address objAddress - a local reference to the Address
  40. */
  41. protected $objAddress;
  42. /**
  43. *@var ShippingMethod objSelectedMethod - the selected method for this module
  44. */
  45. protected $objSelectedMethod;
  46. /**
  47. *@var array aryShippingMethods - the active methods available for this module
  48. */
  49. protected $aryShippingMethods;
  50. protected $strDefaultCarrier;
  51. protected $strDefaultServiceType;
  52. protected $fltShippingTotal;
  53. protected $blnHasActiveMethods=true;
  54. protected $blnIsInternational=false;
  55. protected $intPreviousAddressId;
  56. /**
  57. * This is a mapping of shipping provider to method. Each ShippingMethod has a title
  58. * field, this is displayed at the top of a block showing each method active for a provider.
  59. * The title field is used as the title of the block, so the map is in the form title => objShippingMethodController
  60. * the view containing a radiobutton for the selection of that method.
  61. *@var array aryShippingProviders - an array/map of Carriers to methods
  62. */
  63. public $aryShippingProviders;
  64. /**
  65. *@var AddressSelectionModule objAddressSelectionModule - handles selecting the shipping address
  66. */
  67. public $objAddressSelectionModule;
  68. /**
  69. *@var QTextBox txtNotes - customer comments added to order ..
  70. */
  71. public $txtNotes;
  72. /**
  73. * Module constructor
  74. *@param QPanel objControlBlock - the main control block for this module, usually CheckOutModule
  75. *@param Order objOrder - the order to be shipped ...
  76. */
  77. public function __construct($pnlParentObject, $objControlBlock, Order $objOrder, $blnDebug = false ){
  78. try {
  79. parent::__construct($pnlParentObject );
  80. } catch (QCallerException $objExc) {
  81. $objExc->IncrementOffset();
  82. throw $objExc;
  83. }
  84. if($blnDebug)
  85. $this->blnDebug = $blnDebug;
  86. $this->strDefaultCarrier = DEFAULT_SHIPPING_CARRIER;
  87. $this->strDefaultServiceType = DEFAULT_SHIPPING_SERVICE;
  88. //normally refers to the CheckOutModule ..
  89. $this->objControlBlock =& $objControlBlock;
  90. $this->objOrder =& $objOrder;
  91. $this->AutoRenderChildren = true;
  92. $this->strTemplate = __QUINTA_CORE_VIEWS__ . '/ShippingModule.tpl.php';
  93. $this->initMethodViews();
  94. if( !empty($this->aryShippingMethods)){
  95. $this->objAddressSelectionModule = new AddressSelectionModule($this,
  96. 'SelectAddress',
  97. $this->objOrder->ShippingAddressId
  98. );
  99. $this->objAddress = $this->objAddressSelectionModule->Address;
  100. }else
  101. $this->blnHasActiveMethods = false;
  102. $this->txtNotes = new QTextBox($this);
  103. $this->txtNotes->TextMode = QTextMode::MultiLine;
  104. $this->txtNotes->Columns = 30;
  105. }
  106. /**
  107. * This function initializes the array of potential shipping methods
  108. *@todo make the local pickup option configurable, currently you have to change the check here ..
  109. */
  110. protected function initShippingMethods(){
  111. if( $this->objOrder->IsInternational)
  112. $this->aryShippingMethods = ShippingMethod::QueryArray( QQ::AndCondition(
  113. QQ::Equal(QQN::ShippingMethod()->Active, true),
  114. QQ::Equal(QQN::ShippingMethod()->IsInternational,true)
  115. ));
  116. else
  117. $this->aryShippingMethods = ShippingMethod::QueryArray(
  118. QQ::Equal(QQN::ShippingMethod()->Active, true)
  119. );
  120. if(ZoneType::Colorado == $this->objOrder->ShippingZoneId )
  121. $this->aryShippingMethods[] = ShippingMethod::Load(1);
  122. }
  123. /**
  124. * This function creates a radio button to display for each active shipping method as
  125. * appropriate - if the method is not international no button will be created for an international
  126. * order and if a method is not available or returns a 0 rate charge it will also not be created.
  127. * @todo
  128. * - check availability
  129. * - implement try/catch to handle errors, log them when not debugging.
  130. */
  131. protected function initMethodViews(){
  132. $this->initShippingMethods();
  133. if( empty($this->aryShippingMethods))
  134. return;
  135. if( is_array($this->aryShippingProviders) ){
  136. foreach( $this->aryShippingProviders as $strName => &$aryMethodViews ){
  137. foreach($aryMethodViews as $it => $objMethodView){
  138. $strControlId = $objMethodView->ControlId;
  139. $this->RemoveChildControl($strControlId, true);
  140. unset($aryMethodViews[$it]);
  141. }
  142. unset($this->aryShippingProviders[$strName]);
  143. }
  144. $this->aryShippingProviders = array();
  145. }
  146. foreach($this->aryShippingMethods as $objShippingMethod){
  147. //Fedex Ground international only goes for Canada
  148. if($this->objOrder->IsInternational && CountryType::Canada != $this->objOrder->ShippingCountryId
  149. && false !== stripos( $objShippingMethod->ServiceType, 'FEDEX_GROUND' ) )
  150. continue;
  151. //Skip the Fedex international methods for domestic orders ..
  152. if( !$this->objOrder->IsInternational
  153. && ( false !== stripos( $objShippingMethod->ServiceType, 'GLOBAL' )
  154. || false !== stripos( $objShippingMethod->ServiceType, 'INTERNATIONAL' )) )
  155. continue;
  156. $objShippingMethod->Init($this->objOrder);
  157. ///@todo check availability ..
  158. /* if( ! $objShippingMethod->MethodAvailable() ) continue;*/
  159. $objShippingMethod->GetRate();
  160. /**
  161. *@todo figure this out - USPS, eg. provides no clear way to determine availability (in fact i can't even
  162. * find their !@#$ing error codes ..) so for now if there is no charge we assume it is not available ..
  163. */
  164. if( ! $objShippingMethod->IsAvailable || $objShippingMethod->HasErrors || 0 == $objShippingMethod->Rate ){
  165. // if($this->blnDebug && 'FEDEX_2_DAY' != $objShippingMethod->ServiceType)
  166. if($this->blnDebug)
  167. // exit(var_dump($objShippingMethod));
  168. die($objShippingMethod->Title . ', '
  169. . $objShippingMethod->ServiceType . '<br /> '
  170. . $objShippingMethod->Errors );
  171. else
  172. continue;
  173. }
  174. //eh, could be a server error .. skip it. todo: make me smarter ..
  175. if( ! is_numeric($objShippingMethod->Rate) )
  176. continue;
  177. $objShippingMethodController = new ShippingMethodController($this, $objShippingMethod);
  178. //set the defaults here - note that if the default method is not active this leaves everything
  179. //null until/unless the user selects a method; hence default should be properly configured.
  180. if( $objShippingMethod->Carrier == $this->strDefaultCarrier
  181. && $objShippingMethod->ServiceType == $this->strDefaultServiceType )
  182. {
  183. $objShippingMethodController->Checked = true;
  184. $this->objSelectedMethod = $objShippingMethod;
  185. $this->objOrder->ShippingMethodId = $objShippingMethod->Id;
  186. $this->objOrder->ShippingCharged = $objShippingMethod->Rate;
  187. }
  188. //store by title for the method display ..
  189. $this->aryShippingProviders[$objShippingMethod->Title][] = $objShippingMethodController;
  190. }
  191. }
  192. /**
  193. * This Function is called when the user selects a method - it sets objSelectedMethod
  194. * and updates ShippingMethodId and ShippingCharged in the Order ..
  195. *@param integer intShippingMethodId - the id of the selected method
  196. */
  197. public function SelectMethod($intShippingMethodId){
  198. foreach($this->aryShippingMethods as $objMethod )
  199. if($intShippingMethodId == $objMethod->Id){
  200. //this is redundant .. todo: pick one way or the other?
  201. $this->objSelectedMethod = $objMethod;
  202. $this->objOrder->ShippingMethodId = $objMethod->Id;
  203. $this->objOrder->ShippingCharged = $objMethod->Rate;
  204. if($this->objControlBlock instanceof CheckOutModule)
  205. $this->objControlBlock->RefreshOrderTotalsController();
  206. break;
  207. }
  208. }
  209. public function SelectAddress($intAddressId, $strParameter=null){
  210. if( is_numeric($intAddressId) ){
  211. $this->intPreviousAddressId = $intAddressId;
  212. $this->objOrder->SetShippingAddress($this->objAddressSelectionModule->Address);
  213. $this->objAddress = $this->objAddressSelectionModule->Address;
  214. }
  215. $this->objAddressSelectionModule->RemoveChildControls(true);
  216. $this->RemoveChildControl($this->objAddressSelectionModule->ControlId, false);
  217. if( 'Edit' == $strParameter )
  218. $this->objAddressSelectionModule = new AddressSelectionModule($this, 'SelectAddress', $intAddressId, true);
  219. elseif( 'New' == $strParameter )
  220. $this->objAddressSelectionModule = new AddressSelectionModule($this, 'SelectAddress', null, true);
  221. else{//Note: includes Save and Cancel .
  222. if($intAddressId)
  223. $this->objAddressSelectionModule = new AddressSelectionModule($this, 'SelectAddress', $intAddressId);
  224. else
  225. $this->objAddressSelectionModule = new AddressSelectionModule($this, 'SelectAddress', $this->intPreviousAddressId);
  226. //Refresh the options listing ..
  227. $this->initMethodViews();
  228. }
  229. $this->objAddressSelectionModule->Visible = true;
  230. $this->AddChildControl($this->objAddressSelectionModule);
  231. }
  232. /**
  233. * This Function is called when any input is sent - on failure the
  234. * fields are redrawn with optional error messages.
  235. */
  236. public function Validate(){
  237. $blnToReturn = true;
  238. // validate input here
  239. return $blnToReturn;
  240. }
  241. public function __get($strName){
  242. switch ($strName){
  243. case 'Notes':
  244. return $this->txtNotes->Text ;
  245. case 'SelectedMethod':
  246. return $this->objSelectedMethod ;
  247. case 'Address':
  248. return $this->objAddress ;
  249. case 'HasActiveMethods':
  250. return $this->blnHasActiveMethods ;
  251. case 'IsInternational':
  252. return $this->blnIsInternational ;
  253. case 'DefaultCarrier':
  254. return $this->strDefaultCarrier ;
  255. case 'DefaultServiceType':
  256. return $this->strDefaultServiceType ;
  257. default:
  258. try {
  259. return parent::__get($strName);
  260. } catch (QCallerException $objExc) {
  261. $objExc->IncrementOffset();
  262. throw $objExc;
  263. }
  264. }
  265. }
  266. public function __set($strName, $mixValue){
  267. switch ($strName){
  268. case 'IsInternational':
  269. try {
  270. return ($this->blnIsInternational = QType::Cast($mixValue, Qtype::Boolean ));
  271. } catch (QInvalidCastException $objExc) {
  272. $objExc->IncrementOffset();
  273. throw $objExc;
  274. }
  275. case 'DefaultCarrier':
  276. try {
  277. return ($this->strDefaultCarrier = QType::Cast($mixValue, Qtype::String ));
  278. } catch (QInvalidCastException $objExc) {
  279. $objExc->IncrementOffset();
  280. throw $objExc;
  281. }
  282. case 'DefaultServiceType':
  283. try {
  284. return ($this->strDefaultServiceType = QType::Cast($mixValue, Qtype::String ));
  285. } catch (QInvalidCastException $objExc) {
  286. $objExc->IncrementOffset();
  287. throw $objExc;
  288. }
  289. default:
  290. try {
  291. return (parent::__set($strName, $mixValue));
  292. } catch (QCallerException $objExc) {
  293. $objExc->IncrementOffset();
  294. throw $objExc;
  295. }
  296. }
  297. }
  298. }//end class
  299. }//end define
  300. ?>