Wie man das PrestaShop-Override-System nutzt, ohne den Kern zu beschädigen

Das PrestaShop-Override-System ermöglicht es Ihnen, das Kernverhalten anzupassen, ohne die Originaldateien zu verändern. Bei unachtsamer Anwendung entstehen jedoch instabile Shops, die bei Updates nicht mehr funktionieren. Dieser Leitfaden von Knowband erklärt die Funktionsweise von Overrides, wann sie sinnvoll sind und welche drei wichtigen Fallstricke jeder Entwickler kennen sollte.

Was ist das Override-System?

PrestaShop nutzt PHP-Vererbung, um Ihnen zu ermöglichen, Ihre eigene Version jeder Kernklasse einzubinden, ohne die Originale zu verändern. Jede Kernklasse trägt das Suffix „Core“, beispielsweise „ProductCore“. Wenn Sie die Datei `/override/classes/Product.php` mit einer Klasse namens „Product“ erstellen, die von „ProductCore“ erbt, lädt PrestaShop Ihre Version. Dasselbe gilt für Controller.

Überschreiben Sie nur die Methoden, die Sie ändern müssen, und kopieren Sie niemals eine ganze Klassendatei. Dadurch bleibt Ihr Code schlank und es treten deutlich seltener Konflikte mit zukünftigen Aktualisierungen auf.

Beispiel: So geben Sie den Namen des aktuellen Controllers in Ihrer Header-Vorlage an:

Wo Sie Ihre Überschreibungsdateien ablegen sollen

Überschreibungen können an zwei Orten gespeichert werden. Das globale Verzeichnis `/override/` im Stammverzeichnis Ihrer Installation ist sofort nach dem Leeren des Caches aktiv. Es empfiehlt sich jedoch, Überschreibungen in einem Modul zu bündeln.

Wenn eine Überschreibung innerhalb eines Moduls platziert wird (z. B. /modules/mymodule/override/classes/Product.php), kopiert PrestaShop diese bei der Installation nach /override/ und entfernt sie bei der Deinstallation vollständig. Dies ist die empfohlene Vorgehensweise.

Ein kurzes Beispiel für eine Klassenüberschreibung. Angenommen, Sie möchten den Namen des aktuellen Controllers in Ihrer Header-Vorlage verfügbar machen:

// /override/classes/controllers/FrontController.php

class FrontController extends FrontControllerCore {

    public function initHeader() {

        self::$smarty->assign('currentController', get_class($this));

        return parent::initHeader();

    }}

Rufen Sie immer die übergeordnete Methode auf, es sei denn, Sie haben einen triftigen Grund, dies nicht zu tun. Andernfalls wird die Kernfunktionalität unbemerkt beeinträchtigt.

Überschreiben von Modulklassen und Controllern

Seit PrestaShop 1.6 können Sie auch Modulklassen überschreiben. Hängen Sie dazu „Override“ an den Klassennamen an (z. B. BlockUserInfoOverride, abgeleitet von BlockUserInfo) und legen Sie die Datei unter /override/modules/ ab. Das gleiche Vorgehen gilt für Modul-Controller. Nach jeder Überschreibung müssen Sie den Cache leeren, insbesondere die Datei /cache/class_index.php löschen, da die Änderung sonst nicht wirksam wird.

Wann man Überschreibungen NICHT verwenden sollte

Überschreibungen sind exklusiv – es darf nur eine pro Klasse geben. Sie sind außerdem in Marktplatzmodulen verboten und sollten für Anpassungen im privaten Shop reserviert bleiben. Bevor Sie eine Überschreibung vornehmen, ziehen Sie eine der folgenden Alternativen in Betracht:

  • Haken: PrestaShop löst bei jedem Seitenaufruf Hook-Ereignisse aus – Sie können diese von einem Modul aus nutzen, um benutzerdefinierten Code auszuführen oder Inhalte auf die Seite zu übertragen, ohne sich auch nur in die Nähe einer Kernklasse begeben zu müssen.
  • Symfony-Service-Überschreibungen: Teile von PrestaShop, die auf Symfony migriert wurden, ermöglichen einen übersichtlicheren Umgang mit dem Service-Container. Anstatt die Klasse selbst zu verändern, können Sie einen Service anpassen oder ersetzen.
  • Benutzerdefiniertes Objektmodell: Müssen Sie einer Kernentität Felder hinzufügen? Erstellen Sie Ihr eigenes Objektmodell mit eigener Tabelle, anstatt das bestehende zu patchen.

Sicherheit der Überschreibungen durch Updates

PrestaShop-Updates können Methodensignaturen unbemerkt ändern, sodass Ihre überschriebenen Methoden auf veralteten Annahmen basieren. Überprüfen Sie daher jede überschriebene Methode nach jedem Update. Kommentieren Sie die Änderungen und deren Gründe, prüfen Sie das Änderungsprotokoll und halten Sie die Überschreibung so klein wie möglich. Eine schlanke, gut kommentierte Überschreibung, die die übergeordneten Methoden korrekt aufruft, übersteht Aktualisierungen problemlos. Eine vollständige Klassenkopie hingegen birgt ein Fehlerrisiko.

Der „Geister“-Cache: Warum Ihre Überschreibung unsichtbar erscheint

Der häufigste Grund, warum Entwickler glauben, ihre Überschreibung sei fehlerhaft: PrestaShop durchsucht /override/ nicht bei jeder Anfrage. Stattdessen wird eine Klassenzuordnung in /cache/class_index.php kompiliert. Solange diese Datei nicht gelöscht und neu generiert wird, ist Ihre Überschreibung unsichtbar, egal wie korrekt der Code ist. Das Leeren des Smarty-Caches reicht nicht aus. Löschen Sie class_index.php manuell per FTP/SSH, über „Erweiterte Parameter > Leistung“ im Backend oder über die Symfony-Konsole.

Abbildung 1: Der PrestaShop-Override-Systemablauf und die Rolle von /cache/class_index.php.

 

Die Symfony-Mauer: Legacy-Überschreibungen funktionieren nicht für moderne Controller

Dieses veraltete PHP-System funktioniert nur mit der älteren ObjectModel-Schicht von PrestaShop. Ab PS 1.7 wurden wichtige Bereiche des Backends, darunter die neuen Produktseiten und Bestellbildschirme, in Symfony neu geschrieben. Das PHP-Override-System hat keinen Einfluss auf diese Symfony-Controller. Für diese Zwecke verwenden Sie Service DekorationSie können einen Dienst über die services.yml-Datei Ihres Moduls einbinden oder ersetzen. Falls eine Überschreibung auch nach dem Löschen der class_index.php-Datei keine Wirkung zeigt, ist der Controller wahrscheinlich auf Symfony migriert.

Konfliktlösung: Wenn zwei Module denselben Kurs belegen wollen

Wenn zwei Module dieselbe Klasse überschreiben, gibt PrestaShop bei der zweiten Installation eine Fehlermeldung aus und deaktiviert die Überschreibung. Die Lösung ist manuell: Öffnen Sie beide Dateien, führen Sie alle Methoden in einer einzigen Datei im Verzeichnis `/override/` zusammen, die die Core-Klasse erweitert, und löschen Sie anschließend die Datei `class_index.php`. Fügen Sie Kommentare hinzu, die angeben, welches Modul welche Methode beigesteuert hat. Aus diesem Grund sind Überschreibungen in Marktplatzmodulen nicht zulässig; Konflikte werden so zum Problem des Händlers. Verwenden Sie für andere Anwendungen Hooks.

Abbildung 2: Wie Überschreibungskonflikte entstehen, wenn zwei Module auf dieselbe Klasse abzielen, und die manuelle Zusammenführungslösung.

Über Knowband

Knowband ist ein vertrauenswürdiger Anbieter von E-Commerce-Plugins und -Erweiterungen mit jahrelanger Erfahrung in der Unterstützung von Online-Händlern beim Aufbau schnellerer, intelligenterer und funktionsreicherer Shops. Egal, ob Sie Ihren Shop auf einer Webplattform betreiben. PrestaShop, OpenCartShopify, WooCommerce, MagentoOb Sie nun eine Anwendung auf einer beliebigen anderen großen Plattform verwenden oder nicht – Knowband bietet eine breite Palette an sofort einsatzbereiten Plugins und Erweiterungen, mit denen Sie die Funktionalität verbessern, die Leistung steigern und ein besseres Einkaufserlebnis bieten können, ohne dass Sie von Grund auf neu entwickeln müssen.

Wie kopiere ich den Warenkorb in einen anderen Warenkorb, um den id_cart zu ändern?

Ziele des Blogs

Nach dem Lesen dieses Blogs können wir den Warenkorb in einen anderen Warenkorb kopieren, um die Warenkorb-ID zu ändern.

Anforderung

Wir müssen die folgende Datei ändern, um das Problem zu beheben. Wir werden für dieselbe Änderung in der Core-Datei vornehmen.

Wurzelverzeichnis \ classes \ PaymentModule.php

Verfahren

Wir müssen die Funktion validate order in der PaymentModule-Klassendatei ändern. Da diese Funktion nur einmal während des Bestellvorgangs aufgerufen wird. Wir haben also den Code im selben hinzugefügt, um die Erstellung mehrerer Wagen zu vermeiden.

if (self :: DEBUG_MODE) {PrestaShopLogger :: addLog ('PaymentModule :: validateOrder - Funktion aufgerufen', 1, null, 'Cart', (int) $ id_cart, true); } if (! isset ($ this-> context)) {$ this-> context = Kontext :: getContext (); } $ this-> context-> cart = neuer Warenkorb ((int) $ id_cart);

Der nachstehende Code muss in der Funktion zum Bestätigen der Reihenfolge nach den obigen Zeilen hinzugefügt werden.

// Änderungen gestartet // Um ​​den sicheren Schlüssel der aktuellen Warenkorb-ID zu speichern und ihn dem neuen Warenkorb erneut zuzuweisen. $ old_cart_secure_key = $ this-> context-> cart-> secure_key; // Um ​​die Kundennummer der aktuellen Warenkorb-ID zu speichern und sie dem neuen Warenkorb erneut zuzuweisen. $ Old_cart_customer_id = (int) $ this-> context-> cart-> id_customer; // So entfernen Sie den Kunden aus dem alten Warenkorb $ this-> context-> cart-> id_customer = 0; // Um ​​den Einkaufswagen zu aktualisieren, $ this-> context-> cart-> save (); // Abrufen der aktuellen Warenkorbprodukte $ cart_products = $ this-> context-> cart-> getProducts (); // Neues Einkaufswagenobjekt erstellen $ this-> context-> cart = new Cart (); $ this-> context-> cart-> id_lang = $ this-> context-> language-> id; $ this-> context-> cart-> id_currency = $ this-> context-> currency-> id; // um einen neuen Einkaufswagen hinzuzufügen $ this-> context-> cart-> add (); // um den neuen Warenkorb zu aktualisieren ($ cart_products als $ product) {$ this-> context-> cart-> updateQty ((int) $ product ['Quantity'], (int) $ product ['id_product']), (int) $ product ['id_product_attribute']); } if ($ this-> context-> cookie-> id_guest) {$ guest = neuer Gast ($ this-> context-> cookie-> id_guest); $ this-> context-> cart-> mobile_theme = $ guest-> mobile_theme; } // um den neuen Einkaufswagen mit dem Kunden $ this-> context-> cart-> id_customer = $ old_cart_customer_id abzubilden; // um den neuen Warenkorb zu speichern $ this-> context-> cart-> save (); if ($ this-> context-> cart-> id) {$ this-> context-> cookie-> id_cart = (int) $ this-> context-> cart-> id; $ this-> context-> cookie-> write (); } // um den $ id_cart mit dem des neuen cart zu aktualisieren $ id_cart = (int) $ this-> context-> cart-> id; $ this-> context-> cart-> secure_key = $ old_cart_secure_key; // wechselt um

Nach dem oben beschriebenen Verfahren sieht die Warenkorbliste folgendermaßen aus, dh sie hat einen doppelten Wagen mit den gleichen Daten wie der Wagen, mit dem die Bestellung aufgegeben wurde.

Zusammenfassung

Nach dem oben beschriebenen Verfahren können Sie den Warenkorb in einen anderen Warenkorb kopieren, um den id_cart zu ändern.


Erhalten Sie die neuesten Produkte ohne Sonderpreise (Opencart 2.3)

Ziele des Blogs

Nach dem Lesen dieses Blogs können Sie Produkte aus der Opencart-Datenbank auf der Grundlage von Parametern abrufen, die von der URL übergeben werden.

Anforderung

Die neuesten Produkte müssen auf der Grundlage der übergebenen Parameter in der URL angezeigt werden. Mit diesem Parameter müssen wir entscheiden, ob wir spezielle Produkte anzeigen müssen oder nicht.

Code-Implementierung

Um dies umzusetzen, müssen wir Änderungen an Controller und Modell vornehmen. Im Controller haben wir die Bedingung zum Übergeben von Parametern hinzugefügt, und im Modell nehmen wir entsprechend dem übergebenen Parameter Änderungen in der Abfrage vor. Um den Zustand zu behandeln, den wir verwenden show_special Parameter im Code. Unten ist der Code, den wir im Controller hinzufügen müssen (Katalog / Controller / Erweiterung / Modul / latest.php):

/ * Überprüfung des show_special-Schlüssels im get-Parameter * Dieser Schlüssel ($ show_special) wird zum bedingten Abrufen von Daten aus der Datenbank verwendet. * dh wenn $ show_special = 1 dann Produkte anzeigt, die speziell sind * $ show_special = 0, dann Produkte anzeigen, die nicht speziell sind * else * Alle Produkte anzeigen, unabhängig von speziellen * / if (isset ($ this-> request-> get [' show_special '])) {// Übergabe des Werts in Variable, wenn in der URL $ show_special = $ this-> request-> get [' show_special '] angegeben; } else {// Andernfalls wird ein leerer String übergeben. $ show_special = ''; } // Erstellen einer Variablen, die an das Modell übergeben werden soll $ filter_data = array ('start' => ($ page - 1) * $ limit, 'limit' => $ limit, 'show_special' => $ show_special); // Aufruf einer Modellfunktion zum Abrufen von Daten aus der Datenbank gemäß den im Array filter_data übergebenen Daten. $ results = $ this-> model_catalog_product-> getProductLatest ($ filter_data);

In diesem Code haben wir zuerst überprüft, ob 'show_special'Parameter ist in der URL festgelegt oder nicht. Wenn ja, dann setze den Wert des Parameters in einer Variablen $ show_special und wenn nein, dann Variable als leer setzen. Dann haben wir ein Array erstellt ($ default_data) mit den Werten, für die wir Produkte aus der Datenbank abrufen müssen. Dann haben wir die Modellfunktion aufgerufen getProductLatest () neueste Produkte entsprechend der übergebenen Variablen abrufen ($ filter_data) in der Funktion. Hier ist die Funktion, die wir in model hinzufügen müssen (Katalog / Modell / Katalog / Produkt):

public function getProductLatest ($ data = array ()) {// Parameter für $ join und $ condition setzen, um Daten von speziellen Produkten abzurufen, wenn ($ data ['show_special'] == '1') {$ join = "LEFT JOIN" . DB_PREFIX. "product_special ps ON (ps.product_id = p.product_id)"; $ condition = "AND ps.customer_group_id = '". (int) $ this-> config-> get ('config_customer_group_id'). "'AND ((ps.date_start =' 0000-00-00 ') ODER ps.date_start <JETZT ()) AND (ps.date_end =' 0000-00-00 'ODER ps.date_end> JETZT () ODER ps.date_end = '0001-01-01')) "; } // Einstellung der Parameter $ join und $ condition, um Daten von Produkten abzurufen, die nicht spezifiziert sind elseif ($ data ['show_special'] == '0') {$ join = "LEFT JOIN". DB_PREFIX. "product_special ps ON (ps.product_id = p.product_id)"; $ condition = "AND ps.product_id ist NULL"; } else {// Einstellung von $ join- und $ condition-Parametern zum Abrufen von Daten aller Produkte unabhängig von speziellen $ join = ''; $ condition = ''; } // Abfrage zum Abrufen von Produktdaten aus der Datenbank. Hinzufügen von $ join- und $ condition-Parametern in der Abfrage, um Daten entsprechend abzurufen. $ Sql ​​= "SELECT DISTINCT p.product_id FROM". DB_PREFIX. "Produkt p LEFT JOIN". DB_PREFIX. "product_description pd ON (p.product_id = pd.product_id) LEFT JOIN". DB_PREFIX. "product_to_store p2s ON (p.product_id = p2s.product_id) $ join WHERE p.status = '1' UND p.date_available <= NOW () AND p2s.store_id = '". (int) $ this-> config-> get ('config_store_id'). "'$ condition GROUP BY p.product_id ORDER BY p.product_id DESC"; // Grenzwert in Abfrage setzen, wenn LIMIT vom Controller übergeben wird. if (isset ($ data ['start']) || isset ($ data ['limit'])) {if ($ data ['start'] <0) {$ data ['start'] = 0; } if ($ data ['limit'] <1) {$ data ['limit'] = 20; } $ sql. = "LIMIT". (int) $ data ['start']. ",". (int) $ data ['limit']; } $ product_data = array (); // Abfrage ausführen $ query = $ this-> db-> query ($ sql); // Schleife zum Festlegen abgerufener Daten in der Variablen, die im Controller übergeben werden sollen. foreach ($ query-> lines as $ result) {$ product_data [$ result ['product_id']] = $ this-> getProduct ($ result ['product_id']); } // Liefert Produktdaten an den Controller. Rückgabe von $ product_data; }

Im Modell haben wir Variablen gesetzt $ join und $ Bedingung auf der Grundlage der folgenden drei Bedingungen:

  1. Wenn show_special 1 ist: In diesem Fall haben wir nur Produkte angezeigt, die speziell sind. Da in opencart die Daten von Spezialprodukten in der oc_product_special-Tabelle gespeichert sind, haben wir den Join der oc_product-Tabelle mit der oc_product_secial-Tabelle hinzugefügt, um nur Daten von Spezialprodukten abzurufen.
  2. Wenn show_special 0 ist: In diesem Fall haben wir Produkte angezeigt, die nicht als besonders gekennzeichnet sind. Um also Produkte zu finden, die nicht besonders sind, müssen wir Produkte abrufen, die sich in einer speziellen Tabelle befinden, aber in der oc_product_special-Tabelle. Dazu haben wir den LEFT-Join der oc_product-Tabelle mit der oc_product_secial-Tabelle hinzugefügt und die Bedingung "ps.product_id IS NULL" hinzugefügt.
  3. Wenn show_special nicht 0 und 1 ist: In diesem Fall müssen wir alle Produkte abrufen, ob sie speziell sind oder nicht. Deshalb haben wir Daten direkt aus der oc_product-Tabelle abgerufen.

Nachdem wir alle drei möglichen Bedingungen hinzugefügt haben, haben wir eine Abfrage geschrieben und ausgeführt. Dann haben wir alle abgerufenen Daten in gesetzt $ product_data Array und geben Sie das gleiche an den Controller zurück.

Hinweis: um die neuesten Produkte abzurufen, haben wir in der Abfrage 'Order by product_id DESC' verwendet. Wenn Sie Daten von Produkten abrufen möchten, die in den letzten Tagen hinzugefügt wurden, können Sie in der WHERE-Klausel eine weitere Bedingung hinzufügen, z. B. date_added> 'any date'.

Zusammenfassung

Nach dem oben beschriebenen Verfahren können Sie die neuesten Spezialprodukte auf der Grundlage der übergebenen Parameter für Spezialprodukte in der URL abrufen.


Wie berechnet man den kostenlosen Versand basierend auf dem endgültigen Gesamtpreis in Prestashop?

Nachdem wir diesen Blog gelesen haben, können wir den kostenlosen Versand basierend auf dem endgültigen Gesamtpreis des Einkaufswagens berechnen.

Problem Statement

Wenn wir derzeit Produkte in den Warenkorb legen und zum Kassenbereich gehen, um Rabatt und Versand zu erhalten, sehen wir, dass der Rabatt nach dem Versand gilt. Sagen wir,

  • Der Gesamtbetrag des Einkaufswagens beträgt 100 USD, die versandkostenfrei sind, und ein Rabatt von 5 % wird ebenfalls hinzugefügt
  • Dann, nachdem der Rabatt-Gesamtbetrag des Warenkorbs auf unter 100 $ festgelegt wurde, gilt dennoch der kostenlose Versand, was nicht passieren sollte. Kostenloser Versand muss für den endgültigen Gesamtbetrag des Einkaufswagens nach dem Rabatt gelten.
  • Dies ist der Standardprozess von Prestashop. Siehe Screenshot unten, wo der Versand kostenlos sein muss, wenn die Bestellsumme > 50 ist, aber es gilt der kostenlose Versand, während die Bestellsumme < 50 nach Rabatt ist –

Empfohlen zu lesen: Strategien für kostenlosen Versand: Warum anbieten? Wie bieten? Wann anbieten?

Warum gibt es die aktuelle Ausgabe?

Das aktuelle Problem tritt auf, weil der Versandbetrag anhand des Gesamtbetrags der Bestellung berechnet wird, der vor Anwendung des Rabatts in den Warenkorb gelegt wird. Sie können die folgenden Codezeilen überprüfen, in denen die Bestellsumme ohne Rabattbetrag zur Berechnung der Versandkosten verwendet wird –

$shipping_cost += $carrier->getDeliveryPriceByPrice($the_total_price, $id_zone, (int)$this->id_currency);

Anforderung

Wir müssen die unten erwähnte Datei ändern, um dasselbe zu beheben. Wir werden die entsprechenden Zeilen der Klassendatei überschreiben, um Änderungen in den Core-Dateien zu vermeiden.

Unten ist die Liste der Klassendateien, die überschrieben werden.

1. Stammverzeichnis\classes\Cart.PHP

Verfahren zur Lösung des Problems-

Befolgen Sie die unten aufgeführten Schritte –

1. Rewrite-Funktion�erhaltenPaketVersandkosten

2. Nach der unten angegebenen Zeile –

// Bestellsumme in Standardwährung ohne Gebühren $order_total = $this->getOrderTotal(true, Cart::BOTH_WITHOUT_SHIPPING, $product_list);

3. Fügen Sie die folgende Anweisung hinzu –

$listeDiscounts = $this->getCartRules(); $total_discounts = 0; if (is_array($listeDiscounts)) { if (isset($listeDiscounts[0]['value_real'])) $total_discounts = $listeDiscounts[0]['value_real']; } $price_to_apply_shipment = floatval($order_total) - floatval($total_discounts); Empfohlene Lektüre: Beste kostenlose Versandmanager-Erweiterung für OpenCart Store

4. Ersetzen Sie die unten genannten Zeilen –

Ersetzen Sie diese Zeile

$check_delivery_price_by_price = Spediteur::checkDeliveryPriceByPrice($row['id_carrier'], $total_order, (int)$id_zone, (int)$this->id_currency);

Mit der

$check_delivery_price_by_price = Spediteur::checkDeliveryPriceByPrice($row['id_carrier'], $price_to_apply_shipment, (int)$id_zone, (int)$this->id_currency);

————UND—————–

Ersetzen Sie diese Zeile

$shipping_cost += $carrier->getDeliveryPriceByPrice($the_total_price, $id_zone, (int)$this->id_currency);

Mit der

$shipping_cost += $carrier->getDeliveryPriceByPrice($price_to_apply_shipment, $id_zone, (int)$this->id_currency);

5. Leeren Sie den Cache und führen Sie ihn aus.

Sehen Sie sich den Screenshot unten an, nachdem Sie Änderungen am Code vorgenommen haben –

Nachdem Sie das obige Verfahren befolgt haben, können Sie den Versand für den endgültigen Gesamtbetrag des Warenkorbs berechnen.

Fazit

Beim Online-Kauf spielt die gesamte Preisstruktur für die Kunden eine entscheidende Rolle. Einer der Faktoren, die Ihre Conversions beeinflussen oder beeinträchtigen können, ist die Verfügbarkeit von kostenlosem Versand. Eine Methode zur Ermittlung der Versandoptionen und -kosten auf einer E-Commerce-Website ist die Kostenloses Versandmanager-Addon von Knowband. Offenheit in der Transaktion ist eine der wichtigsten Voraussetzungen zur Steigerung des Durchschnittsumsatzes.

Die Shops, die dieses kostenlose Versandmodul verwendeten, steigerten den Website-Traffic, den Umsatz und die Konversionsraten. Mit Hilfe dieses kostenlosen Versand-Plugins können Shop-Administratoren die Richtlinien und Bedingungen für die Versandkosten definieren. Noch besser, diese Funktion kann vom Shop-Administrator zur Website hinzugefügt oder von ihr entfernt werden.

Der Schwellenwert, ab dem kostenloser Versand angeboten wird, kann von Online-Händlern festgelegt werden. Infolgedessen werden Kunden sogar dazu ermutigt, größere Einkäufe zu tätigen, um sich für den kostenlosen Versand zu qualifizieren. Um mehr über die Funktionalität des kostenlosen Versandmoduls zu erfahren, kontaktieren Sie uns bitte unter support@knowband.com