kotori web solutions Maren Arnhold


Magento: Versandkosten abhängig von der Zahlungsweise berechnen

Speziell für den Einsatz im deutschsprachigen Bereich kann es immer wieder sinnvoll bzw. sogar notwendig werden, die Versandkosten beim Checkout in einem Magento-Shop in variabler Höhe festzulegen - je nachdem, welche Zahlungsweise zuvor gewählt wurde. Denkbar ist zum Beispiel ein Szenario, in dem Vorkassebestellungen mit einem erhöhten Versandkostensatz belegt werden, weil sie einen stärkeren Bearbeitungsaufwand erfordern, oder auch das Berücksichtigen der zusätzlichen Kosten für die Versendung der Ware per Nachnahme, die dann auf den Kunden umgelegt werden sollen. Leider sieht Magento als US-Produkt diese Funktionalität standardmäßig nicht vor, wie auch einige andere für europäische Zwecke schwer entbehrliche Features, z. B. Bankeinzug oder auch die Unterdrückung von Telefonnummern und Bundesstaat als Pflichtangaben im Checkout. Hierfür müssen regelmäßig Extensions eingesetzt werden (wie zusätzliche Zahlungsweisen hinzugefügt werden, wird noch Thema eines künftigen Wissensartikels sein).

Mit einer kleinen Extension, die - kurz gesagt - hauptsächlich die Klasse Mage_Sales_Model_Quote_Address_Total_Abstract erweitert, lässt sich das Versandkostenproblem allerdings recht unkompliziert in den Griff bekommen. Wir gehen im folgenden davon aus, dass unter /app/code/local bereits eine Basis-Extension namens Kotori/Myextension installiert ist, die zwar bereits von Magento erkannt wird, aber sonst keine weiteren Funktionen bereitstellt. Außerdem wird für die Zwecke dieser Demonstration vorausgesetzt, dass im Backend mehrere Zahlungsweisen verfügbar gemacht sind, von denen eine "Vorkasse" lautet bzw. diese Zeichenkette in ihrem Titel trägt (siehe im Backend System -> Verkäufe -> Zahlungsarten). Fakultativ, aber "nice to have" ist auch die Einbindung eines deutschen Sprachpakets, etwa von http://www.magentocommerce.com/translations, was in diesem Beispiel heruntergeladen und aktiviert ist.

Im Verzeichnis /app/code/local/Kotori/Myextension erstellen wir das Unterverzeichnis Model (Groß- und Kleinschreibung beachten!) und legen darin folgende Datei Shipping.php ab:
001:
002:
003:
004:
005:
006:
007:
008:
009:
010:
011:
012:
013:
014:
015:
016:
017:
018:
019:
020:
021:
022:
023:
024:
025:
026:
027:
028:
029:
030:
031:
032:
033:
034:
035:
036:
037:
038:
039:
040:
041:
042:
043:
044:
045:
046:
047:
048:
049:
050:
051:
052:
053:
054:
055:
056:
057:
058:
059:
060:
061:
062:
063:
064:
065:
066:
067:
068:
069:
070:
071:
072:
073:
074:
075:
076:
077:
078:
079:
080:
081:
082:
083:
084:
085:
086:
087:
088:
089:
090:
091:
092:
093:
094:
095:
096:
097:
098:
099:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
<?php
/**
 * Magento
 *
 * NOTICE OF LICENSE
 *
 * This source file is subject to the Open Software License (OSL 3.0)
 * that is bundled with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://opensource.org/licenses/osl-3.0.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@magentocommerce.com so we can send you a copy immediately.
 *
 */
 
 
class Kotori_Myextension_Model_Shipping extends Mage_Sales_Model_Quote_Address_Total_Abstract
{
    public function __construct()
    {
        $this->setCode('shipping');
    }
 
    /**
     * Collect totals information about shipping
     *
     * @param   Mage_Sales_Model_Quote_Address $address
     * @return  Mage_Sales_Model_Quote_Address_Total_Shipping
     */
    public function collect(Mage_Sales_Model_Quote_Address $address)
    {
        parent::collect($address);
 
        $oldWeight = $address->getWeight();
        $address->setWeight(0);
        $address->setFreeMethodWeight(0);
        $this->_setAmount(0)
            ->_setBaseAmount(0);
 
        $items = $this->_getAddressItems($address);
        if (!count($items)) {
            return $this;
        }
 
        $method     = $address->getShippingMethod();
        $freeAddress= $address->getFreeShipping();
 
        $addressWeight      = $address->getWeight();
        $freeMethodWeight   = $address->getFreeMethodWeight();
 
        $addressQty = 0;
 
        foreach ($items as $item) {
            /**
             * Skip if this item is virtual
             */
            if ($item->getProduct()->isVirtual()) {
                continue;
            }
 
            /**
             * Children weight we calculate for parent
             */
            if ($item->getParentItem()) {
                continue;
            }
 
            if ($item->getHasChildren() && $item->isShipSeparately()) {
                foreach ($item->getChildren() as $child) {
                    if ($child->getProduct()->isVirtual()) {
                        continue;
                    }
                    $addressQty += $child->getTotalQty();
 
                    if (!$item->getProduct()->getWeightType()) {
                        $itemWeight = $child->getWeight();
                        $itemQty    = $child->getTotalQty();
                        $rowWeight  = $itemWeight*$itemQty;
                        $addressWeight += $rowWeight;
                        if ($freeAddress || $child->getFreeShipping()===true) {
                            $rowWeight = 0;
                        } elseif (is_numeric($child->getFreeShipping())) {
                            $freeQty = $child->getFreeShipping();
                            if ($itemQty>$freeQty) {
                                $rowWeight = $itemWeight*($itemQty-$freeQty);
                            }
                            else {
                                $rowWeight = 0;
                            }
                        }
                        $freeMethodWeight += $rowWeight;
                        $item->setRowWeight($rowWeight);
                    }
                }
                if ($item->getProduct()->getWeightType()) {
                    $itemWeight = $item->getWeight();
                    $rowWeight  = $itemWeight*$item->getQty();
                    $addressWeight+= $rowWeight;
                    if ($freeAddress || $item->getFreeShipping()===true) {
                        $rowWeight = 0;
                    } elseif (is_numeric($item->getFreeShipping())) {
                        $freeQty = $item->getFreeShipping();
                        if ($item->getQty()>$freeQty) {
                            $rowWeight = $itemWeight*($item->getQty()-$freeQty);
                        }
                        else {
                            $rowWeight = 0;
                        }
                    }
                    $freeMethodWeight+= $rowWeight;
                    $item->setRowWeight($rowWeight);
                }
            }
            else {
                if (!$item->getProduct()->isVirtual()) {
                    $addressQty += $item->getQty();
                }
                $itemWeight = $item->getWeight();
                $rowWeight  = $itemWeight*$item->getQty();
                $addressWeight+= $rowWeight;
                if ($freeAddress || $item->getFreeShipping()===true) {
                    $rowWeight = 0;
                } elseif (is_numeric($item->getFreeShipping())) {
                    $freeQty = $item->getFreeShipping();
                    if ($item->getQty()>$freeQty) {
                        $rowWeight = $itemWeight*($item->getQty()-$freeQty);
                    }
                    else {
                        $rowWeight = 0;
                    }
                }
                $freeMethodWeight+= $rowWeight;
                $item->setRowWeight($rowWeight);
            }
        }
 
        if (isset($addressQty)) {
            $address->setItemQty($addressQty);
        }
 
        $address->setWeight($addressWeight);
        $address->setFreeMethodWeight($freeMethodWeight);
 
        $address->collectShippingRates();
 
        $this->_setAmount(0)
            ->_setBaseAmount(0);
 
        $method = $address->getShippingMethod();
 
        if ($method) {
            foreach ($address->getAllShippingRates() as $rate) {
                if ($rate->getCode()==$method) {
                    $amountPrice = $address->getQuote()->getStore()->convertPrice($rate->getPrice(), false);
 
					// Vorkasse beruecksichtigen!
 
					try {
 
						$zahlungsweise = Mage::getSingleton('checkout/session')->getQuote()->
								getPayment()->getMethodInstance()->getTitle(); 
 
					} catch (Exception $e) { };
 
					if (strpos($zahlungsweise,'Vorkasse') !== false) {
 
						$this->_setAmount(6.5);$this->_setBaseAmount(6.5);
 
					} 	
 
					else { $this->_setAmount($amountPrice); $this->_setBaseAmount($rate->getPrice()); };
 
 
                    $shippingDescription = $rate->getCarrierTitle() . ' - ' . $rate->getMethodTitle();
                    $address->setShippingDescription(trim($shippingDescription, ' -'));
                    break;
                }
            }
        }
 
        return $this;
    }
 
    /**
     * Add shipping totals information to address object
     *
     * @param   Mage_Sales_Model_Quote_Address $address
     * @return  Mage_Sales_Model_Quote_Address_Total_Shipping
     */
    public function fetch(Mage_Sales_Model_Quote_Address $address)
    {
        $amount = $address->getShippingAmount();
        if ($amount != 0 || $address->getShippingDescription()) {
            $title = Mage::helper('sales')->__('Shipping & Handling');
            if ($address->getShippingDescription()) {
                $title .= ' (' . $address->getShippingDescription() . ')';
            }
            $address->addTotal(array(
                'code' => $this->getCode(),
                'title' => $title,
                'value' => $address->getShippingAmount()
            ));
        }
        return $this;
    }
 
    /**
     * Get Shipping label
     *
     * @return string
     */
    public function getLabel()
    {
        return Mage::helper('sales')->__('Shipping');
    }
}
 

Gegenüber der originalen Magento-Klasse, die durch Kotori_Myextension_Model_Shipping erweitert wird, wurden die Zeilen 157-172 hinzugefügt. Die Funktionsweise ist trivial erkennbar: Aus der statischen Klassenmethode Mage::getSingleton wird über eine Kette von weiteren Funktionsaufrufen die Bezeichnung der momentan gewählten Zahlungsweise ermittelt (diese wird im Backend festgelegt!) und in $zahlungsweise abgelegt. Das Ganze wird noch in einen try/catch-Block zur Ausnahmenbehandlung geklammert, da es natürlich auch Situationen gibt, wo die Zahlungsweise beim Aufruf von collect(Mage_Sales_Model_Quote_Address $address) noch gar nicht feststeht, zum Beispiel in der Warenkorbansicht während des Shoppens. Jetzt können wir ganz einfach mit der strpos()-Methode überprüfen, ob ein bestimmter String (oder auch mehrere), hier: "Vorkasse", in der augenblicklichen Zahlungsweise vorkommt, und gegebenenfalls die Versandkosten auf einen beliebigen Wert abwandeln. Dies geschieht mit den Methoden $this->_setAmount() und $this->_setBaseAmount().

Die Datei /app/code/local/Kotori/Myextension/etc/config.xml passen wir nun noch folgendermaßen an, damit Magento unsere Klasse auch anstelle seiner eigenen lädt:
01:
02:
03:
04:
05:
06:
07:
08:
09:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
<?xml version="1.0"?>
<config>
	<modules>
		<Kotori_Myextension>
			<version>0.1.0</version>
		</Kotori_Myextension>
	</modules>
	<global>
		<blocks>
		</blocks>
		<models>
			<sales>
				<rewrite>
					<quote_address_total_shipping>
					Kotori_Myextension_Model_Shipping
					</quote_address_total_shipping>
				</rewrite>
			</sales>
		</models>		
		<events>
		</events>
	</global>
	<frontend>
		<routers>
		</routers>
	</frontend>
	<default>
	</default>	
</config>
 


Abb. 1: Der normale Festbetrag für die Versandkosten ist hier auf 4 Euro festgelegt.
Schauen wir einmal, ob Magento sich erwartungsgemäß verhält. In Abbildung 1 sehen wir, dass der normale Festbetrag für die Versandkosten hier auf 4 Euro festgelegt ist. Wir wählen zunächst Zahlung per Scheck (Abbildung 2) und erhalten im nächsten Schritt eine Abrechnung über insgesamt 26 Euro (22 Euro Warenwert + 4 Euro Versandkosten, wie in Abbildung 3 zu sehen). Gehen wir nun einen Schritt zurück und wählen Vorkasse aus (Abbildung 4), so ergibt sich in der Abrechnung ein anderes Bild: Hier wird jetzt ein Versandkostenaufschlag von 6,50 Euro berechnet, so dass sich ein Gesamtbetrag von 28,50 Euro ergibt (Abbildung 5). Unsere Extension läuft also erfreulich rund!
Autorin: Maren Arnhold


Abb. 2: Zahlungsweise Scheck auswählen...

Abb. 3: ... und der Versandkostenaufschlag wird normal berechnet.

Abb. 4: Mit der Zahlungsweise Vorkasse...

Abb. 5: ... erhöht sich der Aufschlag hingegen wie geplant.

comments powered by Disqus
ANZEIGE
kotori web solutions Maren Arnhold bietet einen Komplettservice rund um Webdesign, Webprogrammierung und Webhosting. Suchen Sie nach einer Lösung für Ihre private Homepage? Möchten Sie ein eigenes Blog betreiben und suchen dafür ein geeignetes CMS und entsprechenden Webspace? Oder interessieren Sie sich für E-Commerce und benötigen einen Webshop? Dann sollten wir uns kennenlernen - eine kostenlose Erstberatung ist selbstverständlich!
© 2013 kotori web solutions Maren Arnhold. Alle Rechte vorbehalten/All rights reserved..