PageRenderTime 67ms CodeModel.GetById 24ms RepoModel.GetById 3ms app.codeStats 0ms

/app/code/core/Mage/Checkout/Model/Type/Multishipping.php

https://github.com/FiveDigital/magento2
PHP | 614 lines | 388 code | 53 blank | 173 comment | 70 complexity | 18fe9ffebd991fb8338f2540f8bcb90f MD5 | raw file
Possible License(s): CC-BY-SA-3.0
  1. <?php
  2. /**
  3. * Magento
  4. *
  5. * NOTICE OF LICENSE
  6. *
  7. * This source file is subject to the Open Software License (OSL 3.0)
  8. * that is bundled with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://opensource.org/licenses/osl-3.0.php
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@magentocommerce.com so we can send you a copy immediately.
  14. *
  15. * DISCLAIMER
  16. *
  17. * Do not edit or add to this file if you wish to upgrade Magento to newer
  18. * versions in the future. If you wish to customize Magento for your
  19. * needs please refer to http://www.magentocommerce.com for more information.
  20. *
  21. * @category Mage
  22. * @package Mage_Checkout
  23. * @copyright Copyright (c) 2012 Magento Inc. (http://www.magentocommerce.com)
  24. * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
  25. */
  26. /**
  27. * Multishipping checkout model
  28. *
  29. * @category Mage
  30. * @package Mage_Checkout
  31. * @author Magento Core Team <core@magentocommerce.com>
  32. */
  33. class Mage_Checkout_Model_Type_Multishipping extends Mage_Checkout_Model_Type_Abstract
  34. {
  35. /**
  36. * Quote shipping addresses items cache
  37. *
  38. * @var array
  39. */
  40. protected $_quoteShippingAddressesItems;
  41. /**
  42. * Constructor
  43. */
  44. public function __construct()
  45. {
  46. parent::__construct();
  47. $this->_init();
  48. }
  49. /**
  50. * Initialize multishipping checkout.
  51. * Split virtual/not virtual items between default billing/shipping addresses
  52. *
  53. * @return Mage_Checkout_Model_Type_Multishipping
  54. */
  55. protected function _init()
  56. {
  57. /**
  58. * reset quote shipping addresses and items
  59. */
  60. $quote = $this->getQuote();
  61. if (!$this->getCustomer()->getId()) {
  62. return $this;
  63. }
  64. if ($this->getCheckoutSession()->getCheckoutState() === Mage_Checkout_Model_Session::CHECKOUT_STATE_BEGIN) {
  65. $this->getCheckoutSession()->setCheckoutState(true);
  66. /**
  67. * Remove all addresses
  68. */
  69. $addresses = $quote->getAllAddresses();
  70. foreach ($addresses as $address) {
  71. $quote->removeAddress($address->getId());
  72. }
  73. if ($defaultShipping = $this->getCustomerDefaultShippingAddress()) {
  74. $quote->getShippingAddress()->importCustomerAddress($defaultShipping);
  75. foreach ($this->getQuoteItems() as $item) {
  76. /**
  77. * Items with parent id we add in importQuoteItem method.
  78. * Skip virtual items
  79. */
  80. if ($item->getParentItemId() || $item->getProduct()->getIsVirtual()) {
  81. continue;
  82. }
  83. $quote->getShippingAddress()->addItem($item);
  84. }
  85. }
  86. if ($this->getCustomerDefaultBillingAddress()) {
  87. $quote->getBillingAddress()
  88. ->importCustomerAddress($this->getCustomerDefaultBillingAddress());
  89. foreach ($this->getQuoteItems() as $item) {
  90. if ($item->getParentItemId()) {
  91. continue;
  92. }
  93. if ($item->getProduct()->getIsVirtual()) {
  94. $quote->getBillingAddress()->addItem($item);
  95. }
  96. }
  97. }
  98. $this->save();
  99. }
  100. return $this;
  101. }
  102. /**
  103. * Get quote items assigned to different quote addresses populated per item qty.
  104. * Based on result array we can display each item separately
  105. *
  106. * @return array
  107. */
  108. public function getQuoteShippingAddressesItems()
  109. {
  110. if ($this->_quoteShippingAddressesItems !== null) {
  111. return $this->_quoteShippingAddressesItems;
  112. }
  113. $items = array();
  114. $addresses = $this->getQuote()->getAllAddresses();
  115. foreach ($addresses as $address) {
  116. foreach ($address->getAllItems() as $item) {
  117. if ($item->getParentItemId()) {
  118. continue;
  119. }
  120. if ($item->getProduct()->getIsVirtual()) {
  121. $items[] = $item;
  122. continue;
  123. }
  124. if ($item->getQty() > 1) {
  125. for ($i = 0, $n = $item->getQty(); $i < $n; $i++) {
  126. if ($i == 0) {
  127. $addressItem = $item;
  128. } else {
  129. $addressItem = clone $item;
  130. }
  131. $addressItem->setQty(1)
  132. ->setCustomerAddressId($address->getCustomerAddressId())
  133. ->save();
  134. $items[] = $addressItem;
  135. }
  136. } else {
  137. $item->setCustomerAddressId($address->getCustomerAddressId());
  138. $items[] = $item;
  139. }
  140. }
  141. }
  142. $this->_quoteShippingAddressesItems = $items;
  143. return $items;
  144. }
  145. /**
  146. * Remove item from address
  147. *
  148. * @param int $addressId
  149. * @param int $itemId
  150. * @return Mage_Checkout_Model_Type_Multishipping
  151. */
  152. public function removeAddressItem($addressId, $itemId)
  153. {
  154. $address = $this->getQuote()->getAddressById($addressId);
  155. /* @var $address Mage_Sales_Model_Quote_Address */
  156. if ($address) {
  157. $item = $address->getValidItemById($itemId);
  158. if ($item) {
  159. if ($item->getQty()>1 && !$item->getProduct()->getIsVirtual()) {
  160. $item->setQty($item->getQty()-1);
  161. } else {
  162. $address->removeItem($item->getId());
  163. }
  164. /**
  165. * Require shiping rate recollect
  166. */
  167. $address->setCollectShippingRates((boolean) $this->getCollectRatesFlag());
  168. if (count($address->getAllItems()) == 0) {
  169. $address->isDeleted(true);
  170. }
  171. if ($quoteItem = $this->getQuote()->getItemById($item->getQuoteItemId())) {
  172. $newItemQty = $quoteItem->getQty()-1;
  173. if ($newItemQty > 0 && !$item->getProduct()->getIsVirtual()) {
  174. $quoteItem->setQty($quoteItem->getQty()-1);
  175. } else {
  176. $this->getQuote()->removeItem($quoteItem->getId());
  177. }
  178. }
  179. $this->save();
  180. }
  181. }
  182. return $this;
  183. }
  184. /**
  185. * Assign quote items to addresses and specify items qty
  186. *
  187. * array structure:
  188. * array(
  189. * $quoteItemId => array(
  190. * 'qty' => $qty,
  191. * 'address' => $customerAddressId
  192. * )
  193. * )
  194. *
  195. * @param array $info
  196. * @return Mage_Checkout_Model_Type_Multishipping
  197. */
  198. public function setShippingItemsInformation($info)
  199. {
  200. if (is_array($info)) {
  201. $allQty = 0;
  202. $itemsInfo = array();
  203. foreach ($info as $itemData) {
  204. foreach ($itemData as $quoteItemId => $data) {
  205. $allQty += $data['qty'];
  206. $itemsInfo[$quoteItemId] = $data;
  207. }
  208. }
  209. $maxQty = (int)Mage::getStoreConfig('shipping/option/checkout_multiple_maximum_qty');
  210. if ($allQty > $maxQty) {
  211. Mage::throwException(Mage::helper('Mage_Checkout_Helper_Data')->__('Maximum qty allowed for Shipping to multiple addresses is %s', $maxQty));
  212. }
  213. $quote = $this->getQuote();
  214. $addresses = $quote->getAllShippingAddresses();
  215. foreach ($addresses as $address) {
  216. $quote->removeAddress($address->getId());
  217. }
  218. foreach ($info as $itemData) {
  219. foreach ($itemData as $quoteItemId => $data) {
  220. $this->_addShippingItem($quoteItemId, $data);
  221. }
  222. }
  223. /**
  224. * Delete all not virtual quote items which are not added to shipping address
  225. * MultishippingQty should be defined for each quote item when it processed with _addShippingItem
  226. */
  227. foreach ($quote->getAllItems() as $_item) {
  228. if (!$_item->getProduct()->getIsVirtual() &&
  229. !$_item->getParentItem() &&
  230. !$_item->getMultishippingQty()
  231. ) {
  232. $quote->removeItem($_item->getId());
  233. }
  234. }
  235. if ($billingAddress = $quote->getBillingAddress()) {
  236. $quote->removeAddress($billingAddress->getId());
  237. }
  238. if ($customerDefaultBilling = $this->getCustomerDefaultBillingAddress()) {
  239. $quote->getBillingAddress()->importCustomerAddress($customerDefaultBilling);
  240. }
  241. foreach ($quote->getAllItems() as $_item) {
  242. if (!$_item->getProduct()->getIsVirtual()) {
  243. continue;
  244. }
  245. if (isset($itemsInfo[$_item->getId()]['qty'])) {
  246. if ($qty = (int)$itemsInfo[$_item->getId()]['qty']) {
  247. $_item->setQty($qty);
  248. $quote->getBillingAddress()->addItem($_item);
  249. } else {
  250. $_item->setQty(0);
  251. $quote->removeItem($_item->getId());
  252. }
  253. }
  254. }
  255. $this->save();
  256. Mage::dispatchEvent('checkout_type_multishipping_set_shipping_items', array('quote'=>$quote));
  257. }
  258. return $this;
  259. }
  260. /**
  261. * Add quote item to specific shipping address based on customer address id
  262. *
  263. * @param int $quoteItemId
  264. * @param array $data array('qty'=>$qty, 'address'=>$customerAddressId)
  265. * @return Mage_Checkout_Model_Type_Multishipping
  266. */
  267. protected function _addShippingItem($quoteItemId, $data)
  268. {
  269. $qty = isset($data['qty']) ? (int) $data['qty'] : 1;
  270. //$qty = $qty > 0 ? $qty : 1;
  271. $addressId = isset($data['address']) ? $data['address'] : false;
  272. $quoteItem = $this->getQuote()->getItemById($quoteItemId);
  273. if ($addressId && $quoteItem) {
  274. /**
  275. * Skip item processing if qty 0
  276. */
  277. if ($qty === 0) {
  278. return $this;
  279. }
  280. $quoteItem->setMultishippingQty((int)$quoteItem->getMultishippingQty()+$qty);
  281. $quoteItem->setQty($quoteItem->getMultishippingQty());
  282. $address = $this->getCustomer()->getAddressById($addressId);
  283. if ($address->getId()) {
  284. if (!$quoteAddress = $this->getQuote()->getShippingAddressByCustomerAddressId($address->getId())) {
  285. $quoteAddress = Mage::getModel('Mage_Sales_Model_Quote_Address')->importCustomerAddress($address);
  286. $this->getQuote()->addShippingAddress($quoteAddress);
  287. }
  288. $quoteAddress = $this->getQuote()->getShippingAddressByCustomerAddressId($address->getId());
  289. if ($quoteAddressItem = $quoteAddress->getItemByQuoteItemId($quoteItemId)) {
  290. $quoteAddressItem->setQty((int)($quoteAddressItem->getQty()+$qty));
  291. } else {
  292. $quoteAddress->addItem($quoteItem, $qty);
  293. }
  294. /**
  295. * Require shiping rate recollect
  296. */
  297. $quoteAddress->setCollectShippingRates((boolean) $this->getCollectRatesFlag());
  298. }
  299. }
  300. return $this;
  301. }
  302. /**
  303. * Reimport customer address info to quote shipping address
  304. *
  305. * @param int $addressId customer address id
  306. * @return Mage_Checkout_Model_Type_Multishipping
  307. */
  308. public function updateQuoteCustomerShippingAddress($addressId)
  309. {
  310. if ($address = $this->getCustomer()->getAddressById($addressId)) {
  311. $this->getQuote()->getShippingAddressByCustomerAddressId($addressId)
  312. ->setCollectShippingRates(true)
  313. ->importCustomerAddress($address)
  314. ->collectTotals();
  315. $this->getQuote()->save();
  316. }
  317. return $this;
  318. }
  319. /**
  320. * Reimport customer billing address to quote
  321. *
  322. * @param int $addressId customer address id
  323. * @return Mage_Checkout_Model_Type_Multishipping
  324. */
  325. public function setQuoteCustomerBillingAddress($addressId)
  326. {
  327. if ($address = $this->getCustomer()->getAddressById($addressId)) {
  328. $this->getQuote()->getBillingAddress($addressId)
  329. ->importCustomerAddress($address)
  330. ->collectTotals();
  331. $this->getQuote()->collectTotals()->save();
  332. }
  333. return $this;
  334. }
  335. /**
  336. * Assign shipping methods to addresses
  337. *
  338. * @param array $methods
  339. * @return Mage_Checkout_Model_Type_Multishipping
  340. */
  341. public function setShippingMethods($methods)
  342. {
  343. $addresses = $this->getQuote()->getAllShippingAddresses();
  344. foreach ($addresses as $address) {
  345. if (isset($methods[$address->getId()])) {
  346. $address->setShippingMethod($methods[$address->getId()]);
  347. } elseif (!$address->getShippingMethod()) {
  348. Mage::throwException(Mage::helper('Mage_Checkout_Helper_Data')->__('Please select shipping methods for all addresses'));
  349. }
  350. }
  351. $this->save();
  352. return $this;
  353. }
  354. /**
  355. * Set payment method info to quote payment
  356. *
  357. * @param array $payment
  358. * @return Mage_Checkout_Model_Type_Multishipping
  359. */
  360. public function setPaymentMethod($payment)
  361. {
  362. if (!isset($payment['method'])) {
  363. Mage::throwException(Mage::helper('Mage_Checkout_Helper_Data')->__('Payment method is not defined'));
  364. }
  365. $quote = $this->getQuote();
  366. $quote->getPayment()->importData($payment);
  367. // shipping totals may be affected by payment method
  368. if (!$quote->isVirtual() && $quote->getShippingAddress()) {
  369. $quote->getShippingAddress()->setCollectShippingRates(true);
  370. $quote->setTotalsCollectedFlag(false)->collectTotals();
  371. }
  372. $quote->save();
  373. return $this;
  374. }
  375. /**
  376. * Prepare order based on quote address
  377. *
  378. * @param Mage_Sales_Model_Quote_Address $address
  379. * @return Mage_Sales_Model_Order
  380. * @throws Mage_Checkout_Exception
  381. */
  382. protected function _prepareOrder(Mage_Sales_Model_Quote_Address $address)
  383. {
  384. $quote = $this->getQuote();
  385. $quote->unsReservedOrderId();
  386. $quote->reserveOrderId();
  387. $quote->collectTotals();
  388. $convertQuote = Mage::getSingleton('Mage_Sales_Model_Convert_Quote');
  389. $order = $convertQuote->addressToOrder($address);
  390. $order->setQuote($quote);
  391. $order->setBillingAddress(
  392. $convertQuote->addressToOrderAddress($quote->getBillingAddress())
  393. );
  394. if ($address->getAddressType() == 'billing') {
  395. $order->setIsVirtual(1);
  396. } else {
  397. $order->setShippingAddress($convertQuote->addressToOrderAddress($address));
  398. }
  399. $order->setPayment($convertQuote->paymentToOrderPayment($quote->getPayment()));
  400. if (Mage::app()->getStore()->roundPrice($address->getGrandTotal()) == 0) {
  401. $order->getPayment()->setMethod('free');
  402. }
  403. foreach ($address->getAllItems() as $item) {
  404. $_quoteItem = $item->getQuoteItem();
  405. if (!$_quoteItem) {
  406. throw new Mage_Checkout_Exception(Mage::helper('Mage_Checkout_Helper_Data')->__('Item not found or already ordered'));
  407. }
  408. $item->setProductType($_quoteItem->getProductType())
  409. ->setProductOptions(
  410. $_quoteItem->getProduct()->getTypeInstance()->getOrderOptions($_quoteItem->getProduct())
  411. );
  412. $orderItem = $convertQuote->itemToOrderItem($item);
  413. if ($item->getParentItem()) {
  414. $orderItem->setParentItem($order->getItemByQuoteItemId($item->getParentItem()->getId()));
  415. }
  416. $order->addItem($orderItem);
  417. }
  418. return $order;
  419. }
  420. /**
  421. * Validate quote data
  422. *
  423. * @return Mage_Checkout_Model_Type_Multishipping
  424. */
  425. protected function _validate()
  426. {
  427. $quote = $this->getQuote();
  428. if (!$quote->getIsMultiShipping()) {
  429. Mage::throwException(Mage::helper('Mage_Checkout_Helper_Data')->__('Invalid checkout type.'));
  430. }
  431. /** @var $paymentMethod Mage_Payment_Model_Method_Abstract */
  432. $paymentMethod = $quote->getPayment()->getMethodInstance();
  433. if (!empty($paymentMethod) && !$paymentMethod->isAvailable($quote)) {
  434. Mage::throwException(Mage::helper('Mage_Checkout_Helper_Data')->__('Please specify payment method.'));
  435. }
  436. $addresses = $quote->getAllShippingAddresses();
  437. foreach ($addresses as $address) {
  438. $addressValidation = $address->validate();
  439. if ($addressValidation !== true) {
  440. Mage::throwException(Mage::helper('Mage_Checkout_Helper_Data')->__('Please check shipping addresses information.'));
  441. }
  442. $method= $address->getShippingMethod();
  443. $rate = $address->getShippingRateByCode($method);
  444. if (!$method || !$rate) {
  445. Mage::throwException(Mage::helper('Mage_Checkout_Helper_Data')->__('Please specify shipping methods for all addresses.'));
  446. }
  447. }
  448. $addressValidation = $quote->getBillingAddress()->validate();
  449. if ($addressValidation !== true) {
  450. Mage::throwException(Mage::helper('Mage_Checkout_Helper_Data')->__('Please check billing address information.'));
  451. }
  452. return $this;
  453. }
  454. /**
  455. * Create orders per each quote address
  456. *
  457. * @return Mage_Checkout_Model_Type_Multishipping
  458. */
  459. public function createOrders()
  460. {
  461. $orderIds = array();
  462. $this->_validate();
  463. $shippingAddresses = $this->getQuote()->getAllShippingAddresses();
  464. $orders = array();
  465. if ($this->getQuote()->hasVirtualItems()) {
  466. $shippingAddresses[] = $this->getQuote()->getBillingAddress();
  467. }
  468. try {
  469. foreach ($shippingAddresses as $address) {
  470. $order = $this->_prepareOrder($address);
  471. $orders[] = $order;
  472. Mage::dispatchEvent(
  473. 'checkout_type_multishipping_create_orders_single',
  474. array('order'=>$order, 'address'=>$address)
  475. );
  476. }
  477. foreach ($orders as $order) {
  478. $order->place();
  479. $order->save();
  480. if ($order->getCanSendNewEmailFlag()){
  481. $order->sendNewOrderEmail();
  482. }
  483. $orderIds[$order->getId()] = $order->getIncrementId();
  484. }
  485. Mage::getSingleton('Mage_Core_Model_Session')->setOrderIds($orderIds);
  486. Mage::getSingleton('Mage_Checkout_Model_Session')->setLastQuoteId($this->getQuote()->getId());
  487. $this->getQuote()
  488. ->setIsActive(false)
  489. ->save();
  490. Mage::dispatchEvent('checkout_submit_all_after', array('orders' => $orders, 'quote' => $this->getQuote()));
  491. return $this;
  492. } catch (Exception $e) {
  493. Mage::dispatchEvent('checkout_multishipping_refund_all', array('orders' => $orders));
  494. throw $e;
  495. }
  496. }
  497. /**
  498. * Collect quote totals and save quote object
  499. *
  500. * @return Mage_Checkout_Model_Type_Multishipping
  501. */
  502. public function save()
  503. {
  504. $this->getQuote()->collectTotals()
  505. ->save();
  506. return $this;
  507. }
  508. /**
  509. * Specify BEGIN state in checkout session whot allow reinit multishipping checkout
  510. *
  511. * @return Mage_Checkout_Model_Type_Multishipping
  512. */
  513. public function reset()
  514. {
  515. $this->getCheckoutSession()->setCheckoutState(Mage_Checkout_Model_Session::CHECKOUT_STATE_BEGIN);
  516. return $this;
  517. }
  518. /**
  519. * Check if quote amount is allowed for multishipping checkout
  520. *
  521. * @return bool
  522. */
  523. public function validateMinimumAmount()
  524. {
  525. return !(Mage::getStoreConfigFlag('sales/minimum_order/active')
  526. && Mage::getStoreConfigFlag('sales/minimum_order/multi_address')
  527. && !$this->getQuote()->validateMinimumAmount());
  528. }
  529. /**
  530. * Get notification message for case when multishipping checkout is not allowed
  531. *
  532. * @return string
  533. */
  534. public function getMinimumAmountDescription()
  535. {
  536. $descr = Mage::getStoreConfig('sales/minimum_order/multi_address_description');
  537. if (empty($descr)) {
  538. $descr = Mage::getStoreConfig('sales/minimum_order/description');
  539. }
  540. return $descr;
  541. }
  542. public function getMinimumAmountError()
  543. {
  544. $error = Mage::getStoreConfig('sales/minimum_order/multi_address_error_message');
  545. if (empty($error)) {
  546. $error = Mage::getStoreConfig('sales/minimum_order/error_message');
  547. }
  548. return $error;
  549. }
  550. /**
  551. * Get order IDs created during checkout
  552. *
  553. * @param bool $asAssoc
  554. * @return array
  555. */
  556. public function getOrderIds($asAssoc = false)
  557. {
  558. $idsAssoc = Mage::getSingleton('Mage_Core_Model_Session')->getOrderIds();
  559. return $asAssoc ? $idsAssoc : array_keys($idsAssoc);
  560. }
  561. }