Ein Kunde betreibt einen Shop, in welchem er seine Pasta und noch andere italienische Produkte vertreibt. Nachdem bisher nur Trockenpasta verkauft wurde, sollte nun auch noch frische Pasta verkauft werden. Allerdings ist der Versand von frischer Pasta teurer, da Isolationsmaterial und Kühlmittel beigelegt werden müssen.

Keine große Sache, dachte ich mir. Verschiedene Versandtarife für verschiedene Produkte ist ja etwas, was alle möglichen Shops benötigen, also deckt das der verwendete Shopify-Shop sicher mit seinen Versandtarif-Einstellungen ab.
Die Einrichtung sah auch vielversprechend aus: Versandtarif hinzufügen, Produkte auswählen, die den teureren Versand erfordern (kleines Ärgernis, dass sich keine Gruppen/Kategorien auswählen lassen, sondern nur einzelne Produkte), speichern.
Test: Produkt in den Warenkorb, Versandkosten passen, top!

Die Freude war allerdings von kurzer Dauer: Legt man ein weiteres Produkt in den Warenkorb, welches nicht in diesem Versandtarif enthalten ist, werden beim Checkout die normale Versandgebühr plus die erhöhte Versandgebühr berechnet.

Da im Internet keine Lösung mit Bordmitteln zu finden ist, scheint es für diesen vermeintlich oft vorkommenden Fall wirklich von Shopify keine Lösung zu geben.
Diese Problemstellung ist nur mit zusätzlichen kostenpflichtigen "Apps" oder Eingriff in den Template-Code beizukommen.

Natürlich kommt in meinem Fall nur Letzteres in Betracht... ;-)

Die Code-Schnipsel, die ich im Shopify-Forum gefunden habe, haben bei mir leider alle nicht funktioniert. Das verwendete Theme scheint wohl noch eine Rolle zu spielen.
Die von mir verwendete Lösung sollte jedoch universell mit jedem Template funktionieren.

"Pseudo-Produkt" hinzufügen

Das Versandkostenproblem habe ich dadurch gelöst, dass ich ein Produkt angelegt habe, welches so viel kostet, wie der Differenzbetrag zwischen normalem Versand und Versand mit Kühlmitteln.
Das Produkt habe ich entsprechend "Kühlmittel und Thermoverpackung" genannt, damit der Kunde beim Checkout auch genau sehen kann, was es mit diesem Aufschlag auf sich hat.

Ebenso könnte man ein Produkt "Sperrgutaufschlag" erstellen, welches dann bei unförmigen Produkten hinzugefügt wird.

Das Produkt hat keine Kategorien oder Tags, so dass es im Onlineshop nirgends auftaucht. (außer, jemand sucht per Suchfunktion explizit danach...aber wenn jemand unbedingt Kühlmittel bestellen will, dann soll er halt...)

Am unteren Ende der Produkt-Seite auf "Website SEO bearbeiten" klicken...
 
...und sich den Handle notieren – den benötigen wir später noch.
 

Theme-Code anpassen

Achtung: Aus technischen Gründen haben im folgenden Quelltext zwei hintereinander folgende geschweifte Klammern }} noch ein Leerzeichen dazwischen. Dieses muss unbedingt entfernt werden!
Ebenso kann ich die geschweifte Klammer plus Prozentzeichen nicht im Quelltext anzeigen. Dies wird als {. und .} angezeigt.
Ich empfehle, die syntaxkorrekten Skripte hier herunterzuladen.

Unter "Onlineshop > Themes" beim aktuellen Live-Theme auf "Aktionen > Code bearbeiten" klicken.

Dem Template "Templates/cart.liquid" fügen wir am Ende die Zeile {. include 'cart-frische-ware' .} hinzu, um ein weiteres Template einzubinden, welches wir gleich erstellen:

{. comment .}
  The contents of the cart.liquid template can be found in /sections/cart-template.liquid
{. endcomment .}

{. section 'cart-template' .}


{. include 'cart-frische-ware' .}

Nun im Template-Baum unter "Snippets" ein neues Snippet hinzufügen. Dieses nennen wir cart-frische-ware.liquid

Hier ist der Code dazu:

{{ '//ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js' | script_tag } }

{. assign benoetigt_kuehlmittel = false .}
{. assign hat_kuehlmittel = 0 .}
{. assign product = all_products['kuhlmittel'] .}
{. assign kuehlmittel_id = product.variants.first.id .}

{. for item in cart.items .}
  {. if item.product.tags contains "Kuhlmittel" .}
    {. assign benoetigt_kuehlmittel = true .}
  {. endif .}
  {. if item.id == kuehlmittel_id .}
    {. assign hat_kuehlmittel = item.variant_id .}
  {. endif .}
{. endfor .}

{. if benoetigt_kuehlmittel == true and hat_kuehlmittel == 0 .}
  <script>
  // Erst prüfen, ob das Kühlmittel bereits vorhanden ist, sonst haben wir 2
  jQuery.getJSON('/cart.js', function(cart) {
    var bereits_vorhanden = false;
    for (var x=0; x<cart.items.length; x++) {
      if (cart.items[x].id == {{ kuehlmittel_id } }) bereits_vorhanden = true;
      }
    // Hinzufügen, falls nicht vorhanden
    if (!bereits_vorhanden)
      jQuery.post('/cart/add.js', {
        items: [{ quantity: 1, id: {{ kuehlmittel_id } } }],
                success: function() { window.setTimeout(function(){document.location.href = '/cart';},1000); }
        });    
    } );
  </script>
{. endif .}

{. if benoetigt_kuehlmittel == false and hat_kuehlmittel != 0 .}
  <script>
  jQuery.post('/cart/update.js', {
    updates: { {{ hat_kuehlmittel } } : 0},
        success: function() { document.location.href = '/cart'; }
    });
  </script>
{. endif .}

In der Zeile

{. assign product = all_products['kuhlmittel'] .}
geben wir den zuvor notierten Handle des Pseudo-Produktes ein.

Produkte, die den speziellen Versand erfordern, anpassen

Alle Produkte, mit denen automatisch das Pseudo-Produkt "Kühlmittel" in den Warenkorb gelegt werden soll, müssen nun den Produkt-Tag "Kuhlmittel" haben (habe an der Stelle auf ein Umlaut verzichtet, um Schwierigkeiten im Liquid-Code zuvorzukommen).
Sollte man einen anderen Tag-Namen bevorzugen, ist die Zeile {. if item.product.tags contains "Kuhlmittel" .} im Liquid-Code anzupassen.

Auf diese Art lässt sich direkt beim Produkt hinterlegen, ob beim Versand Gebühren hinzukommen sollen oder nicht.

Fazit

Der hinzugefügte Liquid-Code wird beim Öffnen des Warenkorbes ausgeführt. Deshalb sollte ein Express-Checkout, der direkt vom Produkt zur Bezahlung via payPal führt und den Warenkorb umgeht, deaktiviert werden!

Fügt man nun ein Produkt in den Warenkorb hinzu, welches den Tag "Kuhlmittel" hat und geht in den Warenkorb, so wird automatisch das Produkt "Kühlmittel" hinzugefügt, welches sich übrigens nicht entfernen lässt.
Denn bei jeder Aktualisierung des Warenkorbs wird geprüft, ob der Warenkorb Produkte mit dem Tag "Kuhlmittel" beinhaltet und ob unser "Pseudo-Produkt" bereits im Warenkorb ist und fügt es im Zweifelsfall (wieder) hinzu.

Wenn man es noch schöner haben will, entfernt man den "Entfernen"-Link für das Pseudo-Produkt "Kühlmittel" in der Datei "Sections/cart-template.liquid".

Zu Beginn der Template-Datei:

{. assign product = all_products['kuhlmittel'] .}
{. assign kuehlmittel_id = product.variants.first.id .}

Etwas weiter unten im Code...

Vorher:

<p class="cart__remove">
  <a href="/cart/change?line={{ forloop.index } }&quantity=0" class="text-link text-link--accent" aria-label="{{ 'cart.label.remove' | t: product: item.title } }" data-cart-remove>{{ 'cart.general.remove' | t } }</a>
</p>

Nachher:

<p class="cart__remove">
  {. if item.id != kuehlmittel_id .}
  <a href="/cart/change?line={{ forloop.index } }&quantity=0" class="text-link text-link--accent" aria-label="{{ 'cart.label.remove' | t: product: item.title } }" data-cart-remove>{{ 'cart.general.remove' | t } }</a>
  {. endif .}
</p>
Und dasselbe Spiel könnte man dann auch noch mit dem Mengen-Feld treiben.