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.

380 lines
17 KiB

12 years ago
  1. <?php
  2. require(__DATAGEN_CLASSES__ . '/ShoppingCartGen.class.php');
  3. /**
  4. * The ShoppingCart class defined here represents the "shopping_cart" table
  5. * in the database, and extends from the code generated abstract ShoppingCartGen
  6. * class, which contains all the basic CRUD-type functionality as well as
  7. * basic methods to handle relationships and index-based loading.
  8. *
  9. * This class also provides a factory method to generate an Order object from the
  10. * current contents of the cart - see CreateNewOrder below.
  11. *
  12. * @package Quasi
  13. * @subpackage ORM
  14. *
  15. */
  16. class ShoppingCart extends ShoppingCartGen
  17. {
  18. /**
  19. *@var Order objOrder - a new Order object for the contents of this cart
  20. */
  21. protected $objOrder;
  22. /**
  23. *@var Account objAccount - a new Account object for the contents of this cart
  24. */
  25. protected $objAccount;
  26. /**
  27. *@var boolean blnUsePreviousAddresses - if true, attempt to use address from last order for new order.
  28. */
  29. protected $blnUsePreviousAddresses;
  30. /**
  31. * Default "to string" handler
  32. * Allows pages to _p()/echo()/print() this object, and to define the default
  33. * way this object would be outputted.
  34. *
  35. * Can also be called directly via $objShoppingCart->__toString().
  36. *
  37. * @return string a nicely formatted string representation of this object
  38. */
  39. public function __toString() {
  40. return sprintf('Cart for %s', $this->Account);
  41. }
  42. public static function LoadByAccountId($intAccountId)
  43. {
  44. return ShoppingCart::QuerySingle(QQ::Equal(QQN::ShoppingCart()->AccountId, $intAccountId) );
  45. }
  46. public function AddItem($intProductId)
  47. {
  48. $objSCItem = ShoppingCartItem::LoadByProductIdShoppingCartId($intProductId, $this->Id);
  49. if(!$objSCItem)
  50. {
  51. $objSCItem = new ShoppingCartItem();
  52. $objSCItem->ProductId = $intProductId;
  53. $objSCItem->ShoppingCartId = $this->Id;
  54. }//Note: quantity defaults to 1 on creation ..
  55. else
  56. $objSCItem->Quantity += 1;
  57. $objSCItem->Save();
  58. $this->RefreshCartModule();
  59. }
  60. public function RemoveItem($intProductId)
  61. {
  62. $objSCItem = ShoppingCartItem::LoadByProductIdShoppingCartId($intProductId, $this->Id);
  63. if($objSCItem)
  64. {
  65. $objSCItem->Delete();
  66. $this->RefreshCartModule();
  67. }
  68. }
  69. public function RefreshCartModule()
  70. {
  71. ///@todo - kludge to refresh the module, do this without reloading the page ..
  72. QApplication::Redirect(Quasi::$RequestUri );
  73. /* $objShoppingCartModule = IndexPage::$MainWindow->GetActiveModule('ShoppingCartModule');
  74. if($objShoppingCartModule instanceof ShoppingCartModule)
  75. $objShoppingCartModule->RefreshCart();*/
  76. }
  77. /**
  78. * This function creates a new Order object initilized with the current contents of the cart.
  79. * Note: the order is returned unsaved, both the order and order items are virtual and not
  80. * inserted to the database until Save() is called on the order object.
  81. *
  82. * The Order is returned unsaved with status set to "Shopping". The array of NewOrderItems in the
  83. * new order is loaded with OrderItems created from the ShoppingCart Items.
  84. * If possible we set the Shipping cost based on the default configured shipping method
  85. *
  86. *
  87. * This function uses several subfunctions to:
  88. * - transfer order items from the shopping cart items
  89. * - calculate estimated shipping
  90. * - calculate taxes
  91. * - set the address fields based on the Account addresses
  92. * - set the price totals
  93. * @todo - be able to save order/address state if they go back from checkout
  94. * really the cart box could be merged into the Checkout module
  95. * if($this->objOrder instanceof Order)
  96. * return $this->objOrder;
  97. *@param boolean blnUsePreviousAddresses - if true, use the Addresses from the previous order
  98. * @return Order - an initialized order object
  99. */
  100. public function CreateNewOrder($blnUsePreviousAddresses=false)
  101. {
  102. $this->blnUsePreviousAddresses = $blnUsePreviousAddresses;
  103. if(! $this->AccountId)
  104. throw new QCallerException('CreateOrder called on a ShoppingCart that has no Account Id!');
  105. $this->objOrder = new Order();
  106. $this->objAccount = Account::LoadById($this->AccountId);
  107. $this->initOrderAddresses();
  108. if( $this->objOrder->ShippingCountryId != CountryType::GetId(STORE_COUNTRY) )
  109. $this->objOrder->IsInternational = true;
  110. else
  111. $this->objOrder->IsInternational = false;
  112. $this->objOrder->AccountId = $this->AccountId;
  113. $this->objOrder->StatusId = OrderStatusType::Shopping;
  114. // create the order items from shopping cart ..
  115. $this->initOrderItems();
  116. //try to set a default shipping charge estimate ..
  117. $this->initOrderShippingCharge();
  118. ///@todo this should be more configurable ..
  119. $this->objOrder->HandlingCharged = DEFAULT_HANDLING_CHARGE;
  120. $this->objOrder->InitTax();
  121. return $this->objOrder;
  122. }
  123. /**
  124. * This function initializes the array of OrderItems and also various product totals in the Order.
  125. * The order items are obtained from the ShoppingCartItems, a Product is instatiated for each
  126. * to obtain values added to Order totals.
  127. */
  128. private function initOrderItems()
  129. {
  130. $fltProductTotalCharged = 0.0;
  131. $fltProductTotalCost = 0.0;
  132. $fltMaxX = 0.0;
  133. $fltMaxY = 0.0;
  134. $fltMaxZ = 0.0;
  135. foreach ( $this->GetShoppingCartItemArray() as $objShoppingCartItem )
  136. {
  137. $objItem = new OrderItem();
  138. $objItem->ProductId = $objShoppingCartItem->ProductId;
  139. $objItem->Quantity = $objShoppingCartItem->Quantity;
  140. $this->objOrder->AddNewOrderItem($objItem);
  141. //now, increment order values for each product ..
  142. $objProduct = Product::Load($objItem->ProductId);
  143. //increment total price
  144. $fltProductTotalCharged += $objProduct->RetailPrice * $objItem->Quantity;
  145. $fltProductTotalCost += $objProduct->Cost * $objItem->Quantity;
  146. //increment total weight
  147. $this->objOrder->TotalOunces += $objProduct->Weight;
  148. //increment total max sizes
  149. if($objProduct->Width > $fltMaxX)
  150. $fltMaxX = $objProduct->Width;
  151. if($objProduct->Height > $fltMaxY)
  152. $fltMaxY = $objProduct->Height;
  153. //BUG alert: add thickness, tranlates to "height" in shipping, sorry ..
  154. // this might need fixing, we are assuming things are stacked up and
  155. // that they are thin (like PCB boards ..)
  156. $fltMaxZ += $objProduct->Depth;
  157. }
  158. $this->objOrder->ProductTotalCharged = $fltProductTotalCharged;
  159. $this->objOrder->ProductTotalCost = $fltProductTotalCost;
  160. if($this->objOrder->TotalOunces >= 16)
  161. {
  162. $this->objOrder->TotalPounds = (int) floor( $this->objOrder->TotalOunces / 16 );
  163. $this->objOrder->TotalOunces = ( $this->objOrder->TotalOunces % 16);
  164. }
  165. $this->objOrder->XAxisSize = $fltMaxX;
  166. $this->objOrder->YAxisSize = $fltMaxY;
  167. $this->objOrder->ZAxisSize = $fltMaxZ;
  168. }
  169. /**
  170. * This function determines the default addresses for the Account and place them in Order fields
  171. * for shipping and billing address. These may be modified by the user in the CheckOutEditModule
  172. * The default addresses are those for the Person associated with the Account - note that these
  173. * may be changed by the user, here we are just setting the defaults for initial display.
  174. *
  175. * Note: if CreateNewOrder(true) was called, UsePreviousAddresses will be set true and will trigger
  176. * an attempt to initilize the order using the last used address - if no previous order exists the
  177. * usual default assignment occurs.
  178. */
  179. private function initOrderAddresses()
  180. {
  181. if($this->blnUsePreviousAddresses)
  182. {
  183. $aryClauses = array();
  184. array_push($aryClauses, QQ::OrderBy(QQN::Order()->CreationDate, false) );
  185. $objPreviousOrder = Order::QuerySingle(
  186. QQ::Equal(QQN::Order()->AccountId, $this->AccountId),
  187. $aryClauses
  188. );
  189. //if possible, just copy addresses from previous order ..
  190. if($objPreviousOrder instanceof Order)
  191. {
  192. $this->objOrder = $objPreviousOrder;
  193. $this->objOrder->Id = null;
  194. $this->objOrder->Restored = false;
  195. $this->objOrder->HandlingCharged = null;
  196. $this->objOrder->ProductTotalCharged = null;
  197. $this->objOrder->ShippingCharged = null;
  198. $this->objOrder->ProductTotalCost = null;
  199. $this->objOrder->ShippingCost = null;
  200. $this->objOrder->Tax = null;
  201. $this->objOrder->CreationDate = null;
  202. $this->objOrder->LastModificationDate = null;
  203. $this->objOrder->CompletionDate = null;
  204. $this->objOrder->Notes = null;
  205. $this->objOrder->ShippingMethodId = null;
  206. $this->objOrder->PaymentMethodId = null;
  207. /* Bug alert - these may need to be reset .. however, they are all private
  208. so we would need to override in Order. Leave unless problematic.
  209. $this->objOrder->_objOrderChange = null;
  210. $this->objOrder->_objOrderChangeArray = array();
  211. $this->objOrder->_objOrderItem = null;
  212. $this->objOrder->_objOrderItemArray = array();
  213. $this->objOrder->_objOrderStatusHistory = null;
  214. $this->objOrder->_objOrderStatusHistoryArray = array();
  215. $this->objOrder->_objPaypalTransaction = null;
  216. $this->objOrder->_objPaypalTransactionArray = array();
  217. $this->objOrder->_objTrackingNumber = null;
  218. $this->objOrder->_objTrackingNumberArray = array();
  219. $this->objOrder->__strVirtualAttributeArray = array();
  220. */
  221. //now, attempt to set address ids used ..
  222. $this->objOrder->SetShippingAddress($objPreviousOrder->GetShippingAddress());
  223. $this->objOrder->SetBillingAddress($objPreviousOrder->GetBillingAddress());
  224. return;
  225. }
  226. }
  227. //Otherwise, try to get a shipping address or default to primary ..
  228. $objShippingAddress = Address::QuerySingle( QQ::AndCondition (
  229. QQ::Equal(QQN::Address()->PersonId, $this->objAccount->PersonId ),
  230. QQ::Equal(QQN::Address()->TypeId, AddressType::Shipping)
  231. ) );
  232. if( null === $objShippingAddress )
  233. $objShippingAddress = Address::QuerySingle( QQ::AndCondition (
  234. QQ::Equal(QQN::Address()->PersonId, $this->objAccount->PersonId ),
  235. QQ::Equal(QQN::Address()->TypeId, AddressType::Primary)
  236. ) );
  237. //could be that they entered something different in their account so check for associated persons/addresses ..
  238. if( null === $objShippingAddress )
  239. {
  240. $aryPersonIds = array();
  241. $aryPersons = Person::QueryArray(QQ::Equal(QQN::Person()->OwnerPersonId, $this->objAccount->PersonId));
  242. if(!empty($aryPersons) )
  243. foreach($aryPersons as $objPerson )
  244. $aryPersonIds[] = $objPerson->Id;
  245. else
  246. throw new QCallerException('ShoppingCart: No shipping address found for ' . $this->objAccount);
  247. $objShippingAddress = Address::QuerySingle( QQ::AndCondition (
  248. QQ::In(QQN::Address()->PersonId, $aryPersonIds ),
  249. QQ::In(QQN::Address()->TypeId, array(AddressType::Primary, AddressType::Shipping, AddressType::Billing))
  250. ) );
  251. }
  252. //set the order's fields for shipping
  253. if(null != $objShippingAddress)
  254. $this->objOrder->SetShippingAddress($objShippingAddress);
  255. else
  256. throw new QCallerException('ShoppingCart: No shipping address found for ' . $this->objAccount);
  257. //now try to get a billing address or default to primary ..
  258. $objBillingAddress = Address::QuerySingle( QQ::AndCondition (
  259. QQ::Equal(QQN::Address()->PersonId, $this->objAccount->PersonId ),
  260. QQ::Equal(QQN::Address()->TypeId, AddressType::Billing)
  261. ) );
  262. if( null === $objBillingAddress )
  263. $objBillingAddress = Address::QuerySingle( QQ::AndCondition (
  264. QQ::Equal(QQN::Address()->PersonId, $this->objAccount->PersonId ),
  265. QQ::Equal(QQN::Address()->TypeId, AddressType::Primary)
  266. ) );
  267. if( null === $objBillingAddress )
  268. $objBillingAddress = $objShippingAddress;
  269. //set the order's fields for billing, use shipping as a default if none was found above
  270. if(null != $objBillingAddress)
  271. $this->objOrder->SetBillingAddress($objBillingAddress);
  272. elseif(null != $objShippingAddress)
  273. $this->objOrder->SetBillingAddress($objShippingAddress);
  274. }
  275. /**
  276. * This function attempts to set the shipping charge based on the default address
  277. * and the configured default provider
  278. */
  279. private function initOrderShippingCharge()
  280. {
  281. $objShippingMethod = ShippingMethod::QuerySingle( QQ::AndCondition(
  282. QQ::Equal(QQN::ShippingMethod()->Active, true),
  283. QQ::Equal( QQN::ShippingMethod()->Carrier, DEFAULT_SHIPPING_CARRIER ),
  284. QQ::Equal( QQN::ShippingMethod()->ServiceType, DEFAULT_SHIPPING_SERVICE )
  285. ));
  286. //possible that default method is inactive ..
  287. if( $objShippingMethod instanceof ShippingMethod )
  288. {
  289. $this->objOrder->ShippingMethodId = $objShippingMethod->Id;
  290. $objShippingMethod->Init($this->objOrder);
  291. $this->objOrder->ShippingCharged = $objShippingMethod->GetRate();
  292. if(! $this->objOrder->ShippingCharged )
  293. {//errors .. fallback to default.
  294. $this->objOrder->ShippingCharged = DEFAULT_SHIPPING_RATE;
  295. $this->objOrder->ShippingMethodId = 1;
  296. }
  297. } else {
  298. $this->objOrder->ShippingCharged = DEFAULT_SHIPPING_RATE;
  299. $this->objOrder->ShippingMethodId = 1;
  300. }
  301. }
  302. // Override or Create New Properties and Variables
  303. // For performance reasons, these variables and __set and __get override methods
  304. // are commented out. But if you wish to implement or override any
  305. // of the data generated properties, please feel free to uncomment them.
  306. /*
  307. protected $strSomeNewProperty;
  308. public function __get($strName) {
  309. switch ($strName) {
  310. case 'SomeNewProperty': return $this->strSomeNewProperty;
  311. default:
  312. try {
  313. return parent::__get($strName);
  314. } catch (QCallerException $objExc) {
  315. $objExc->IncrementOffset();
  316. throw $objExc;
  317. }
  318. }
  319. }
  320. public function __set($strName, $mixValue) {
  321. switch ($strName) {
  322. case 'SomeNewProperty':
  323. try {
  324. return ($this->strSomeNewProperty = QType::Cast($mixValue, QType::String));
  325. } catch (QInvalidCastException $objExc) {
  326. $objExc->IncrementOffset();
  327. throw $objExc;
  328. }
  329. default:
  330. try {
  331. return (parent::__set($strName, $mixValue));
  332. } catch (QCallerException $objExc) {
  333. $objExc->IncrementOffset();
  334. throw $objExc;
  335. }
  336. }
  337. }
  338. */
  339. }
  340. ?>