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_Sales
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:
28: /**
29: * Sales Quote address model
30: *
31: * @method Mage_Sales_Model_Resource_Quote_Address _getResource()
32: * @method Mage_Sales_Model_Resource_Quote_Address getResource()
33: * @method int getQuoteId()
34: * @method Mage_Sales_Model_Quote_Address setQuoteId(int $value)
35: * @method string getCreatedAt()
36: * @method Mage_Sales_Model_Quote_Address setCreatedAt(string $value)
37: * @method string getUpdatedAt()
38: * @method Mage_Sales_Model_Quote_Address setUpdatedAt(string $value)
39: * @method int getCustomerId()
40: * @method Mage_Sales_Model_Quote_Address setCustomerId(int $value)
41: * @method int getSaveInAddressBook()
42: * @method Mage_Sales_Model_Quote_Address setSaveInAddressBook(int $value)
43: * @method int getCustomerAddressId()
44: * @method Mage_Sales_Model_Quote_Address setCustomerAddressId(int $value)
45: * @method string getAddressType()
46: * @method Mage_Sales_Model_Quote_Address setAddressType(string $value)
47: * @method string getEmail()
48: * @method Mage_Sales_Model_Quote_Address setEmail(string $value)
49: * @method string getPrefix()
50: * @method Mage_Sales_Model_Quote_Address setPrefix(string $value)
51: * @method string getFirstname()
52: * @method Mage_Sales_Model_Quote_Address setFirstname(string $value)
53: * @method string getMiddlename()
54: * @method Mage_Sales_Model_Quote_Address setMiddlename(string $value)
55: * @method string getLastname()
56: * @method Mage_Sales_Model_Quote_Address setLastname(string $value)
57: * @method string getSuffix()
58: * @method Mage_Sales_Model_Quote_Address setSuffix(string $value)
59: * @method string getCompany()
60: * @method Mage_Sales_Model_Quote_Address setCompany(string $value)
61: * @method string getCity()
62: * @method Mage_Sales_Model_Quote_Address setCity(string $value)
63: * @method Mage_Sales_Model_Quote_Address setRegion(string $value)
64: * @method Mage_Sales_Model_Quote_Address setRegionId(int $value)
65: * @method string getPostcode()
66: * @method Mage_Sales_Model_Quote_Address setPostcode(string $value)
67: * @method string getCountryId()
68: * @method Mage_Sales_Model_Quote_Address setCountryId(string $value)
69: * @method string getTelephone()
70: * @method Mage_Sales_Model_Quote_Address setTelephone(string $value)
71: * @method string getFax()
72: * @method Mage_Sales_Model_Quote_Address setFax(string $value)
73: * @method int getSameAsBilling()
74: * @method Mage_Sales_Model_Quote_Address setSameAsBilling(int $value)
75: * @method int getFreeShipping()
76: * @method Mage_Sales_Model_Quote_Address setFreeShipping(int $value)
77: * @method int getCollectShippingRates()
78: * @method Mage_Sales_Model_Quote_Address setCollectShippingRates(int $value)
79: * @method string getShippingMethod()
80: * @method Mage_Sales_Model_Quote_Address setShippingMethod(string $value)
81: * @method string getShippingDescription()
82: * @method Mage_Sales_Model_Quote_Address setShippingDescription(string $value)
83: * @method float getWeight()
84: * @method Mage_Sales_Model_Quote_Address setWeight(float $value)
85: * @method float getSubtotal()
86: * @method Mage_Sales_Model_Quote_Address setSubtotal(float $value)
87: * @method float getBaseSubtotal()
88: * @method Mage_Sales_Model_Quote_Address setBaseSubtotal(float $value)
89: * @method Mage_Sales_Model_Quote_Address setSubtotalWithDiscount(float $value)
90: * @method Mage_Sales_Model_Quote_Address setBaseSubtotalWithDiscount(float $value)
91: * @method float getTaxAmount()
92: * @method Mage_Sales_Model_Quote_Address setTaxAmount(float $value)
93: * @method float getBaseTaxAmount()
94: * @method Mage_Sales_Model_Quote_Address setBaseTaxAmount(float $value)
95: * @method float getShippingAmount()
96: * @method float getBaseShippingAmount()
97: * @method float getShippingTaxAmount()
98: * @method Mage_Sales_Model_Quote_Address setShippingTaxAmount(float $value)
99: * @method float getBaseShippingTaxAmount()
100: * @method Mage_Sales_Model_Quote_Address setBaseShippingTaxAmount(float $value)
101: * @method float getDiscountAmount()
102: * @method Mage_Sales_Model_Quote_Address setDiscountAmount(float $value)
103: * @method float getBaseDiscountAmount()
104: * @method Mage_Sales_Model_Quote_Address setBaseDiscountAmount(float $value)
105: * @method float getGrandTotal()
106: * @method Mage_Sales_Model_Quote_Address setGrandTotal(float $value)
107: * @method float getBaseGrandTotal()
108: * @method Mage_Sales_Model_Quote_Address setBaseGrandTotal(float $value)
109: * @method string getCustomerNotes()
110: * @method Mage_Sales_Model_Quote_Address setCustomerNotes(string $value)
111: * @method string getDiscountDescription()
112: * @method Mage_Sales_Model_Quote_Address setDiscountDescription(string $value)
113: * @method float getShippingDiscountAmount()
114: * @method Mage_Sales_Model_Quote_Address setShippingDiscountAmount(float $value)
115: * @method float getBaseShippingDiscountAmount()
116: * @method Mage_Sales_Model_Quote_Address setBaseShippingDiscountAmount(float $value)
117: * @method float getSubtotalInclTax()
118: * @method Mage_Sales_Model_Quote_Address setSubtotalInclTax(float $value)
119: * @method float getBaseSubtotalTotalInclTax()
120: * @method Mage_Sales_Model_Quote_Address setBaseSubtotalTotalInclTax(float $value)
121: * @method int getGiftMessageId()
122: * @method Mage_Sales_Model_Quote_Address setGiftMessageId(int $value)
123: * @method float getHiddenTaxAmount()
124: * @method Mage_Sales_Model_Quote_Address setHiddenTaxAmount(float $value)
125: * @method float getBaseHiddenTaxAmount()
126: * @method Mage_Sales_Model_Quote_Address setBaseHiddenTaxAmount(float $value)
127: * @method float getShippingHiddenTaxAmount()
128: * @method Mage_Sales_Model_Quote_Address setShippingHiddenTaxAmount(float $value)
129: * @method float getBaseShippingHiddenTaxAmount()
130: * @method Mage_Sales_Model_Quote_Address setBaseShippingHiddenTaxAmount(float $value)
131: * @method float getShippingInclTax()
132: * @method Mage_Sales_Model_Quote_Address setShippingInclTax(float $value)
133: * @method float getBaseShippingInclTax()
134: * @method Mage_Sales_Model_Quote_Address setBaseShippingInclTax(float $value)
135: *
136: * @category Mage
137: * @package Mage_Sales
138: * @author Magento Core Team <core@magentocommerce.com>
139: */
140: class Mage_Sales_Model_Quote_Address extends Mage_Customer_Model_Address_Abstract
141: {
142: const RATES_FETCH = 1;
143: const RATES_RECALCULATE = 2;
144:
145: /**
146: * Prefix of model events
147: *
148: * @var string
149: */
150: protected $_eventPrefix = 'sales_quote_address';
151:
152: /**
153: * Name of event object
154: *
155: * @var string
156: */
157: protected $_eventObject = 'quote_address';
158:
159: /**
160: * Quote object
161: *
162: * @var Mage_Sales_Model_Quote
163: */
164: protected $_items = null;
165:
166: /**
167: * Quote object
168: *
169: * @var Mage_Sales_Model_Quote
170: */
171: protected $_quote = null;
172:
173: /**
174: * Sales Quote address rates
175: *
176: * @var Mage_Sales_Model_Quote_Address_Rate
177: */
178: protected $_rates = null;
179:
180: /**
181: * Total models collector
182: *
183: * @var Mage_Sales_Model_Quote_Address_Totla_Collector
184: */
185: protected $_totalCollector = null;
186:
187: /**
188: * Total data as array
189: *
190: * @var array
191: */
192: protected $_totals = array();
193:
194: protected $_totalAmounts = array();
195: protected $_baseTotalAmounts = array();
196:
197: /**
198: * Whether to segregate by nominal items only
199: *
200: * @var bool
201: */
202: protected $_nominalOnly = null;
203:
204: /**
205: * Initialize resource
206: */
207: protected function _construct()
208: {
209: $this->_init('sales/quote_address');
210: }
211:
212: /**
213: * Init mapping array of short fields to its full names
214: *
215: * @return Mage_Sales_Model_Quote_Address
216: */
217: protected function _initOldFieldsMap()
218: {
219: $this->_oldFieldsMap = Mage::helper('sales')->getOldFieldMap('quote_address');
220: return $this;
221: }
222:
223: /**
224: * Initialize quote identifier before save
225: *
226: * @return Mage_Sales_Model_Quote_Address
227: */
228: protected function _beforeSave()
229: {
230: parent::_beforeSave();
231: if ($this->getQuote()) {
232: $quoteId = $this->getQuote()->getId();
233: if ($quoteId) {
234: $this->setQuoteId($quoteId);
235: } else {
236: $this->_dataSaveAllowed = false;
237: }
238: $this->setCustomerId($this->getQuote()->getCustomerId());
239: /**
240: * Init customer address id if customer address is assigned
241: */
242: if ($this->getCustomerAddress()) {
243: $this->setCustomerAddressId($this->getCustomerAddress()->getId());
244: }
245: }
246: if ($this->getAddressType() == Mage_Sales_Model_Quote_Address::TYPE_SHIPPING
247: && $this->getSameAsBilling() === null
248: ) {
249: $this->setSameAsBilling(1);
250: }
251: return $this;
252: }
253:
254: /**
255: * Save child collections
256: *
257: * @return Mage_Sales_Model_Quote_Address
258: */
259: protected function _afterSave()
260: {
261: parent::_afterSave();
262: if (null !== $this->_items) {
263: $this->getItemsCollection()->save();
264: }
265: if (null !== $this->_rates) {
266: $this->getShippingRatesCollection()->save();
267: }
268: return $this;
269: }
270:
271: /**
272: * Declare adress quote model object
273: *
274: * @param Mage_Sales_Model_Quote $quote
275: * @return Mage_Sales_Model_Quote_Address
276: */
277: public function setQuote(Mage_Sales_Model_Quote $quote)
278: {
279: $this->_quote = $quote;
280: $this->setQuoteId($quote->getId());
281: return $this;
282: }
283:
284: /**
285: * Retrieve quote object
286: *
287: * @return Mage_Sales_Model_Quote
288: */
289: public function getQuote()
290: {
291: return $this->_quote;
292: }
293:
294: /**
295: * Import quote address data from customer address object
296: *
297: * @param Mage_Customer_Model_Address $address
298: * @return Mage_Sales_Model_Quote_Address
299: */
300: public function importCustomerAddress(Mage_Customer_Model_Address $address)
301: {
302: Mage::helper('core')->copyFieldset('customer_address', 'to_quote_address', $address, $this);
303: $email = null;
304: if ($address->hasEmail()) {
305: $email = $address->getEmail();
306: }
307: elseif ($address->getCustomer()) {
308: $email = $address->getCustomer()->getEmail();
309: }
310: if ($email) {
311: $this->setEmail($email);
312: }
313: return $this;
314: }
315:
316: /**
317: * Export data to customer address object
318: *
319: * @return Mage_Customer_Model_Address
320: */
321: public function exportCustomerAddress()
322: {
323: $address = Mage::getModel('customer/address');
324: Mage::helper('core')->copyFieldset('sales_convert_quote_address', 'to_customer_address', $this, $address);
325: return $address;
326: }
327:
328: /**
329: * Import address data from order address
330: *
331: * @param Mage_Sales_Model_Order_Address $address
332: * @return Mage_Sales_Model_Quote_Address
333: */
334: public function importOrderAddress(Mage_Sales_Model_Order_Address $address)
335: {
336: $this->setAddressType($address->getAddressType())
337: ->setCustomerId($address->getCustomerId())
338: ->setCustomerAddressId($address->getCustomerAddressId())
339: ->setEmail($address->getEmail());
340:
341: Mage::helper('core')->copyFieldset('sales_convert_order_address', 'to_quote_address', $address, $this);
342:
343: return $this;
344: }
345:
346: /**
347: * Convert object to array
348: *
349: * @param array $arrAttributes
350: * @return array
351: */
352: public function toArray(array $arrAttributes = array())
353: {
354: $arr = parent::toArray($arrAttributes);
355: $arr['rates'] = $this->getShippingRatesCollection()->toArray($arrAttributes);
356: $arr['items'] = $this->getItemsCollection()->toArray($arrAttributes);
357: foreach ($this->getTotals() as $k=>$total) {
358: $arr['totals'][$k] = $total->toArray();
359: }
360: return $arr;
361: }
362:
363: /**
364: * Retrieve address items collection
365: *
366: * @return Mage_Eav_Model_Entity_Collection_Abstract
367: */
368: public function getItemsCollection()
369: {
370: if (is_null($this->_items)) {
371: $this->_items = Mage::getModel('sales/quote_address_item')->getCollection()
372: ->setAddressFilter($this->getId());
373:
374: if ($this->getId()) {
375: foreach ($this->_items as $item) {
376: $item->setAddress($this);
377: }
378: }
379: }
380: return $this->_items;
381: }
382:
383: /**
384: * Get all available address items
385: *
386: * @return array
387: */
388: public function getAllItems()
389: {
390: // We calculate item list once and cache it in three arrays - all items, nominal, non-nominal
391: $cachedItems = $this->_nominalOnly ? 'nominal' : ($this->_nominalOnly === false ? 'nonnominal' : 'all');
392: $key = 'cached_items_' . $cachedItems;
393: if (!$this->hasData($key)) {
394: // For compatibility we will use $this->_filterNominal to divide nominal items from non-nominal
395: // (because it can be overloaded)
396: // So keep current flag $this->_nominalOnly and restore it after cycle
397: $wasNominal = $this->_nominalOnly;
398: $this->_nominalOnly = true; // Now $this->_filterNominal() will return positive values for nominal items
399:
400: $quoteItems = $this->getQuote()->getItemsCollection();
401: $addressItems = $this->getItemsCollection();
402:
403: $items = array();
404: $nominalItems = array();
405: $nonNominalItems = array();
406: if ($this->getQuote()->getIsMultiShipping() && $addressItems->count() > 0) {
407: foreach ($addressItems as $aItem) {
408: if ($aItem->isDeleted()) {
409: continue;
410: }
411:
412: if (!$aItem->getQuoteItemImported()) {
413: $qItem = $this->getQuote()->getItemById($aItem->getQuoteItemId());
414: if ($qItem) {
415: $aItem->importQuoteItem($qItem);
416: }
417: }
418: $items[] = $aItem;
419: if ($this->_filterNominal($aItem)) {
420: $nominalItems[] = $aItem;
421: } else {
422: $nonNominalItems[] = $aItem;
423: }
424: }
425: } else {
426: /*
427: * For virtual quote we assign items only to billing address, otherwise - only to shipping address
428: */
429: $addressType = $this->getAddressType();
430: $canAddItems = $this->getQuote()->isVirtual()
431: ? ($addressType == self::TYPE_BILLING)
432: : ($addressType == self::TYPE_SHIPPING);
433:
434: if ($canAddItems) {
435: foreach ($quoteItems as $qItem) {
436: if ($qItem->isDeleted()) {
437: continue;
438: }
439: $items[] = $qItem;
440: if ($this->_filterNominal($qItem)) {
441: $nominalItems[] = $qItem;
442: } else {
443: $nonNominalItems[] = $qItem;
444: }
445: }
446: }
447: }
448:
449: // Cache calculated lists
450: $this->setData('cached_items_all', $items);
451: $this->setData('cached_items_nominal', $nominalItems);
452: $this->setData('cached_items_nonnominal', $nonNominalItems);
453:
454: $this->_nominalOnly = $wasNominal; // Restore original value before we changed it
455: }
456:
457: $items = $this->getData($key);
458: return $items;
459: }
460:
461: /**
462: * Getter for all non-nominal items
463: *
464: * @return array
465: */
466: public function getAllNonNominalItems()
467: {
468: $this->_nominalOnly = false;
469: $result = $this->getAllItems();
470: $this->_nominalOnly = null;
471: return $result;
472: }
473:
474: /**
475: * Getter for all nominal items
476: *
477: * @return array
478: */
479: public function getAllNominalItems()
480: {
481: $this->_nominalOnly = true;
482: $result = $this->getAllItems();
483: $this->_nominalOnly = null;
484: return $result;
485: }
486:
487: /**
488: * Segregate by nominal criteria
489: *
490: * true: get nominals only
491: * false: get non-nominals only
492: * null: get all
493: *
494: * @param Mage_Sales_Model_Quote_Item_Abstract
495: * @return Mage_Sales_Model_Quote_Item_Abstract|false
496: */
497: protected function _filterNominal($item)
498: {
499: return (null === $this->_nominalOnly)
500: || ((false === $this->_nominalOnly) && !$item->isNominal())
501: || ((true === $this->_nominalOnly) && $item->isNominal())
502: ? $item : false;
503: }
504:
505: /**
506: * Retrieve all visible items
507: *
508: * @return array
509: */
510: public function getAllVisibleItems()
511: {
512: $items = array();
513: foreach ($this->getAllItems() as $item) {
514: if (!$item->getParentItemId()) {
515: $items[] = $item;
516: }
517: }
518: return $items;
519: }
520:
521: /**
522: * Retrieve item quantity by id
523: *
524: * @param int $itemId
525: * @return float|int
526: */
527: public function getItemQty($itemId = 0)
528: {
529: if ($this->hasData('item_qty')) {
530: return $this->getData('item_qty');
531: }
532:
533: $qty = 0;
534: if ($itemId == 0) {
535: foreach ($this->getAllItems() as $item) {
536: $qty += $item->getQty();
537: }
538: } else {
539: $item = $this->getItemById($itemId);
540: if ($item) {
541: $qty = $item->getQty();
542: }
543: }
544: return $qty;
545: }
546:
547: /**
548: * Check Quote address has Items
549: *
550: * @return bool
551: */
552: public function hasItems()
553: {
554: return sizeof($this->getAllItems())>0;
555: }
556:
557: /**
558: * Get address item object by id without
559: *
560: * @param int $itemId
561: * @return Mage_Sales_Model_Quote_Address_Item
562: */
563: public function getItemById($itemId)
564: {
565: foreach ($this->getItemsCollection() as $item) {
566: if ($item->getId()==$itemId) {
567: return $item;
568: }
569: }
570: return false;
571: }
572:
573: /**
574: * Get prepared not deleted item
575: *
576: * @param $itemId
577: * @return Mage_Sales_Model_Quote_Address_Item
578: */
579: public function getValidItemById($itemId)
580: {
581: foreach ($this->getAllItems() as $item) {
582: if ($item->getId()==$itemId) {
583: return $item;
584: }
585: }
586: return false;
587: }
588:
589: /**
590: * Retrieve item object by quote item Id
591: *
592: * @param int $itemId
593: * @return Mage_Sales_Model_Quote_Address_Item
594: */
595: public function getItemByQuoteItemId($itemId)
596: {
597: foreach ($this->getItemsCollection() as $item) {
598: if ($item->getQuoteItemId()==$itemId) {
599: return $item;
600: }
601: }
602: return false;
603: }
604:
605: /**
606: * Remove item from collection
607: *
608: * @param int $itemId
609: * @return Mage_Sales_Model_Quote_Address
610: */
611: public function removeItem($itemId)
612: {
613: $item = $this->getItemById($itemId);
614: if ($item) {
615: $item->isDeleted(true);
616: }
617: return $this;
618: }
619:
620: /**
621: * Add item to address
622: *
623: * @param Mage_Sales_Model_Quote_Item_Abstract $item
624: * @param int $qty
625: * @return Mage_Sales_Model_Quote_Address
626: */
627: public function addItem(Mage_Sales_Model_Quote_Item_Abstract $item, $qty=null)
628: {
629: if ($item instanceof Mage_Sales_Model_Quote_Item) {
630: if ($item->getParentItemId()) {
631: return $this;
632: }
633: $addressItem = Mage::getModel('sales/quote_address_item')
634: ->setAddress($this)
635: ->importQuoteItem($item);
636: $this->getItemsCollection()->addItem($addressItem);
637:
638: if ($item->getHasChildren()) {
639: foreach ($item->getChildren() as $child) {
640: $addressChildItem = Mage::getModel('sales/quote_address_item')
641: ->setAddress($this)
642: ->importQuoteItem($child)
643: ->setParentItem($addressItem);
644: $this->getItemsCollection()->addItem($addressChildItem);
645: }
646: }
647: }
648: else {
649: $addressItem = $item;
650: $addressItem->setAddress($this);
651: if (!$addressItem->getId()) {
652: $this->getItemsCollection()->addItem($addressItem);
653: }
654: }
655:
656: if ($qty) {
657: $addressItem->setQty($qty);
658: }
659: return $this;
660: }
661:
662: /**
663: * Retrieve collection of quote shipping rates
664: *
665: * @return Mage_Eav_Model_Entity_Collection_Abstract
666: */
667: public function getShippingRatesCollection()
668: {
669: if (is_null($this->_rates)) {
670: $this->_rates = Mage::getModel('sales/quote_address_rate')->getCollection()
671: ->setAddressFilter($this->getId());
672: if ($this->getQuote()->hasNominalItems(false)) {
673: $this->_rates->setFixedOnlyFilter(true);
674: }
675: if ($this->getId()) {
676: foreach ($this->_rates as $rate) {
677: $rate->setAddress($this);
678: }
679: }
680: }
681: return $this->_rates;
682: }
683:
684: /**
685: * Retrieve all address shipping rates
686: *
687: * @return array
688: */
689: public function getAllShippingRates()
690: {
691: $rates = array();
692: foreach ($this->getShippingRatesCollection() as $rate) {
693: if (!$rate->isDeleted()) {
694: $rates[] = $rate;
695: }
696: }
697: return $rates;
698: }
699:
700: /**
701: * Retrieve all grouped shipping rates
702: *
703: * @return array
704: */
705: public function getGroupedAllShippingRates()
706: {
707: $rates = array();
708: foreach ($this->getShippingRatesCollection() as $rate) {
709: if (!$rate->isDeleted() && $rate->getCarrierInstance()) {
710: if (!isset($rates[$rate->getCarrier()])) {
711: $rates[$rate->getCarrier()] = array();
712: }
713:
714: $rates[$rate->getCarrier()][] = $rate;
715: $rates[$rate->getCarrier()][0]->carrier_sort_order = $rate->getCarrierInstance()->getSortOrder();
716: }
717: }
718: uasort($rates, array($this, '_sortRates'));
719: return $rates;
720: }
721:
722: /**
723: * Sort rates recursive callback
724: *
725: * @param array $a
726: * @param array $b
727: * @return int
728: */
729: protected function _sortRates($a, $b)
730: {
731: if ((int)$a[0]->carrier_sort_order < (int)$b[0]->carrier_sort_order) {
732: return -1;
733: }
734: elseif ((int)$a[0]->carrier_sort_order > (int)$b[0]->carrier_sort_order) {
735: return 1;
736: }
737: else {
738: return 0;
739: }
740: }
741:
742: /**
743: * Retrieve shipping rate by identifier
744: *
745: * @param int $rateId
746: * @return Mage_Sales_Model_Quote_Address_Rate | false
747: */
748: public function getShippingRateById($rateId)
749: {
750: foreach ($this->getShippingRatesCollection() as $rate) {
751: if ($rate->getId()==$rateId) {
752: return $rate;
753: }
754: }
755: return false;
756: }
757:
758: /**
759: * Retrieve shipping rate by code
760: *
761: * @param string $code
762: * @return Mage_Sales_Model_Quote_Address_Rate
763: */
764: public function getShippingRateByCode($code)
765: {
766: foreach ($this->getShippingRatesCollection() as $rate) {
767: if ($rate->getCode()==$code) {
768: return $rate;
769: }
770: }
771: return false;
772: }
773:
774: /**
775: * Mark all shipping rates as deleted
776: *
777: * @return Mage_Sales_Model_Quote_Address
778: */
779: public function removeAllShippingRates()
780: {
781: foreach ($this->getShippingRatesCollection() as $rate) {
782: $rate->isDeleted(true);
783: }
784: return $this;
785: }
786:
787: /**
788: * Add shipping rate
789: *
790: * @param Mage_Sales_Model_Quote_Address_Rate $rate
791: * @return Mage_Sales_Model_Quote_Address
792: */
793: public function addShippingRate(Mage_Sales_Model_Quote_Address_Rate $rate)
794: {
795: $rate->setAddress($this);
796: $this->getShippingRatesCollection()->addItem($rate);
797: return $this;
798: }
799:
800: /**
801: * Collecting shipping rates by address
802: *
803: * @return Mage_Sales_Model_Quote_Address
804: */
805: public function collectShippingRates()
806: {
807: if (!$this->getCollectShippingRates()) {
808: return $this;
809: }
810:
811: $this->setCollectShippingRates(false);
812:
813: $this->removeAllShippingRates();
814:
815: if (!$this->getCountryId()) {
816: return $this;
817: }
818:
819: $found = $this->requestShippingRates();
820: if (!$found) {
821: $this->setShippingAmount(0)
822: ->setBaseShippingAmount(0)
823: ->setShippingMethod('')
824: ->setShippingDescription('');
825: }
826:
827: return $this;
828: }
829:
830: /**
831: * Request shipping rates for entire address or specified address item
832: * Returns true if current selected shipping method code corresponds to one of the found rates
833: *
834: * @param Mage_Sales_Model_Quote_Item_Abstract $item
835: * @return bool
836: */
837: public function requestShippingRates(Mage_Sales_Model_Quote_Item_Abstract $item = null)
838: {
839: /** @var $request Mage_Shipping_Model_Rate_Request */
840: $request = Mage::getModel('shipping/rate_request');
841: $request->setAllItems($item ? array($item) : $this->getAllItems());
842: $request->setDestCountryId($this->getCountryId());
843: $request->setDestRegionId($this->getRegionId());
844: $request->setDestRegionCode($this->getRegionCode());
845: /**
846: * need to call getStreet with -1
847: * to get data in string instead of array
848: */
849: $request->setDestStreet($this->getStreet(-1));
850: $request->setDestCity($this->getCity());
851: $request->setDestPostcode($this->getPostcode());
852: $request->setPackageValue($item ? $item->getBaseRowTotal() : $this->getBaseSubtotal());
853: $packageValueWithDiscount = $item
854: ? $item->getBaseRowTotal() - $item->getBaseDiscountAmount()
855: : $this->getBaseSubtotalWithDiscount();
856: $request->setPackageValueWithDiscount($packageValueWithDiscount);
857: $request->setPackageWeight($item ? $item->getRowWeight() : $this->getWeight());
858: $request->setPackageQty($item ? $item->getQty() : $this->getItemQty());
859:
860: /**
861: * Need for shipping methods that use insurance based on price of physical products
862: */
863: $packagePhysicalValue = $item
864: ? $item->getBaseRowTotal()
865: : $this->getBaseSubtotal() - $this->getBaseVirtualAmount();
866: $request->setPackagePhysicalValue($packagePhysicalValue);
867:
868: $request->setFreeMethodWeight($item ? 0 : $this->getFreeMethodWeight());
869:
870: /**
871: * Store and website identifiers need specify from quote
872: */
873: /*$request->setStoreId(Mage::app()->getStore()->getId());
874: $request->setWebsiteId(Mage::app()->getStore()->getWebsiteId());*/
875:
876: $request->setStoreId($this->getQuote()->getStore()->getId());
877: $request->setWebsiteId($this->getQuote()->getStore()->getWebsiteId());
878: $request->setFreeShipping($this->getFreeShipping());
879: /**
880: * Currencies need to convert in free shipping
881: */
882: $request->setBaseCurrency($this->getQuote()->getStore()->getBaseCurrency());
883: $request->setPackageCurrency($this->getQuote()->getStore()->getCurrentCurrency());
884: $request->setLimitCarrier($this->getLimitCarrier());
885:
886: $request->setBaseSubtotalInclTax($this->getBaseSubtotalInclTax());
887:
888: $result = Mage::getModel('shipping/shipping')->collectRates($request)->getResult();
889:
890: $found = false;
891: if ($result) {
892: $shippingRates = $result->getAllRates();
893:
894: foreach ($shippingRates as $shippingRate) {
895: $rate = Mage::getModel('sales/quote_address_rate')
896: ->importShippingRate($shippingRate);
897: if (!$item) {
898: $this->addShippingRate($rate);
899: }
900:
901: if ($this->getShippingMethod() == $rate->getCode()) {
902: if ($item) {
903: $item->setBaseShippingAmount($rate->getPrice());
904: } else {
905: /**
906: * possible bug: this should be setBaseShippingAmount(),
907: * see Mage_Sales_Model_Quote_Address_Total_Shipping::collect()
908: * where this value is set again from the current specified rate price
909: * (looks like a workaround for this bug)
910: */
911: $this->setShippingAmount($rate->getPrice());
912: }
913:
914: $found = true;
915: }
916: }
917: }
918: return $found;
919: }
920:
921: /**
922: * Get totals collector model
923: *
924: * @return Mage_Sales_Model_Quote_Address_Total_Collector
925: */
926: public function getTotalCollector()
927: {
928: if ($this->_totalCollector === null) {
929: $this->_totalCollector = Mage::getSingleton(
930: 'sales/quote_address_total_collector',
931: array('store'=>$this->getQuote()->getStore())
932: );
933: }
934: return $this->_totalCollector;
935: }
936:
937: /**
938: * Retrieve total models
939: *
940: * @deprecated
941: * @return array
942: */
943: public function getTotalModels()
944: {
945: return $this->getTotalCollector()->getRetrievers();
946: }
947:
948: /**
949: * Collect address totals
950: *
951: * @return Mage_Sales_Model_Quote_Address
952: */
953: public function collectTotals()
954: {
955: Mage::dispatchEvent($this->_eventPrefix . '_collect_totals_before', array($this->_eventObject => $this));
956: foreach ($this->getTotalCollector()->getCollectors() as $model) {
957: $model->collect($this);
958: }
959: Mage::dispatchEvent($this->_eventPrefix . '_collect_totals_after', array($this->_eventObject => $this));
960: return $this;
961: }
962:
963: /**
964: * Get address totals as array
965: *
966: * @return array
967: */
968: public function getTotals()
969: {
970: foreach ($this->getTotalCollector()->getRetrievers() as $model) {
971: $model->fetch($this);
972: }
973: return $this->_totals;
974: }
975:
976: /**
977: * Add total data or model
978: *
979: * @param Mage_Sales_Model_Quote_Total|array $total
980: * @return Mage_Sales_Model_Quote_Address
981: */
982: public function addTotal($total)
983: {
984: if (is_array($total)) {
985: $totalInstance = Mage::getModel('sales/quote_address_total')
986: ->setData($total);
987: } elseif ($total instanceof Mage_Sales_Model_Quote_Total) {
988: $totalInstance = $total;
989: }
990: $totalInstance->setAddress($this);
991: $this->_totals[$totalInstance->getCode()] = $totalInstance;
992: return $this;
993: }
994:
995: /**
996: * Rewrite clone method
997: *
998: * @return Mage_Sales_Model_Quote_Address
999: */
1000: public function __clone()
1001: {
1002: $this->setId(null);
1003: }
1004:
1005: /**
1006: * Validate minimum amount
1007: *
1008: * @return bool
1009: */
1010: public function validateMinimumAmount()
1011: {
1012: $storeId = $this->getQuote()->getStoreId();
1013: if (!Mage::getStoreConfigFlag('sales/minimum_order/active', $storeId)) {
1014: return true;
1015: }
1016:
1017: if ($this->getQuote()->getIsVirtual() && $this->getAddressType() == self::TYPE_SHIPPING) {
1018: return true;
1019: }
1020: elseif (!$this->getQuote()->getIsVirtual() && $this->getAddressType() != self::TYPE_SHIPPING) {
1021: return true;
1022: }
1023:
1024: $amount = Mage::getStoreConfig('sales/minimum_order/amount', $storeId);
1025: if ($this->getBaseSubtotalWithDiscount() < $amount) {
1026: return false;
1027: }
1028: return true;
1029: }
1030:
1031: /**
1032: * Retrieve applied taxes
1033: *
1034: * @return array
1035: */
1036: public function getAppliedTaxes()
1037: {
1038: return unserialize($this->getData('applied_taxes'));
1039: }
1040:
1041: /**
1042: * Set applied taxes
1043: *
1044: * @param array $data
1045: * @return Mage_Sales_Model_Quote_Address
1046: */
1047: public function setAppliedTaxes($data)
1048: {
1049: return $this->setData('applied_taxes', serialize($data));
1050: }
1051:
1052: /**
1053: * Set shipping amount
1054: *
1055: * @param float $value
1056: * @param bool $alreadyExclTax
1057: * @return Mage_Sales_Model_Quote_Address
1058: */
1059: public function setShippingAmount($value, $alreadyExclTax = false)
1060: {
1061: return $this->setData('shipping_amount', $value);
1062: }
1063:
1064: /**
1065: * Set base shipping amount
1066: *
1067: * @param float $value
1068: * @param bool $alreadyExclTax
1069: * @return Mage_Sales_Model_Quote_Address
1070: */
1071: public function setBaseShippingAmount($value, $alreadyExclTax = false)
1072: {
1073: return $this->setData('base_shipping_amount', $value);
1074: }
1075:
1076: /**
1077: * Set total amount value
1078: *
1079: * @param string $code
1080: * @param float $amount
1081: * @return Mage_Sales_Model_Quote_Address
1082: */
1083: public function setTotalAmount($code, $amount)
1084: {
1085: $this->_totalAmounts[$code] = $amount;
1086: if ($code != 'subtotal') {
1087: $code = $code.'_amount';
1088: }
1089: $this->setData($code, $amount);
1090: return $this;
1091: }
1092:
1093: /**
1094: * Set total amount value in base store currency
1095: *
1096: * @param string $code
1097: * @param float $amount
1098: * @return Mage_Sales_Model_Quote_Address
1099: */
1100: public function setBaseTotalAmount($code, $amount)
1101: {
1102: $this->_baseTotalAmounts[$code] = $amount;
1103: if ($code != 'subtotal') {
1104: $code = $code.'_amount';
1105: }
1106: $this->setData('base_'.$code, $amount);
1107: return $this;
1108: }
1109:
1110: /**
1111: * Add amount total amount value
1112: *
1113: * @param string $code
1114: * @param float $amount
1115: * @return Mage_Sales_Model_Quote_Address
1116: */
1117: public function addTotalAmount($code, $amount)
1118: {
1119: $amount = $this->getTotalAmount($code)+$amount;
1120: $this->setTotalAmount($code, $amount);
1121: return $this;
1122: }
1123:
1124: /**
1125: * Add amount total amount value in base store currency
1126: *
1127: * @param string $code
1128: * @param float $amount
1129: * @return Mage_Sales_Model_Quote_Address
1130: */
1131: public function addBaseTotalAmount($code, $amount)
1132: {
1133: $amount = $this->getBaseTotalAmount($code)+$amount;
1134: $this->setBaseTotalAmount($code, $amount);
1135: return $this;
1136: }
1137:
1138: /**
1139: * Get total amount value by code
1140: *
1141: * @param string $code
1142: * @return float
1143: */
1144: public function getTotalAmount($code)
1145: {
1146: if (isset($this->_totalAmounts[$code])) {
1147: return $this->_totalAmounts[$code];
1148: }
1149: return 0;
1150: }
1151:
1152: /**
1153: * Get total amount value by code in base store curncy
1154: *
1155: * @param string $code
1156: * @return float
1157: */
1158: public function getBaseTotalAmount($code)
1159: {
1160: if (isset($this->_baseTotalAmounts[$code])) {
1161: return $this->_baseTotalAmounts[$code];
1162: }
1163: return 0;
1164: }
1165:
1166: /**
1167: * Get all total amount values
1168: *
1169: * @return array
1170: */
1171: public function getAllTotalAmounts()
1172: {
1173: return $this->_totalAmounts;
1174: }
1175:
1176: /**
1177: * Get all total amount values in base currency
1178: *
1179: * @return array
1180: */
1181: public function getAllBaseTotalAmounts()
1182: {
1183: return $this->_baseTotalAmounts;
1184: }
1185:
1186: /**
1187: * Get subtotal amount with applied discount in base currency
1188: *
1189: * @return float
1190: */
1191: public function getBaseSubtotalWithDiscount()
1192: {
1193: return $this->getBaseSubtotal()+$this->getBaseDiscountAmount();
1194: }
1195:
1196: /**
1197: * Get subtotal amount with applied discount
1198: *
1199: * @return float
1200: */
1201: public function getSubtotalWithDiscount()
1202: {
1203: return $this->getSubtotal()+$this->getDiscountAmount();
1204: }
1205: }
1206: