A Qcodo based CMS/ecommerce framework
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.

376 lines
16 KiB

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