Zum Hauptinhalt springen

Storefront-Library

Hintergrund, Motivation und Anforderungen

Die Storefront-Library wurde im Rahmen des Checkout 2.0 Projekts entwickelt.

Hintergrund: Mit Checkout 2.0 wurden die Möglichkeiten zur Rabattierung deutlich erweitert und damit auch komplexer. Unterschiede gegenüber der alten Rabattierung mit Hilfe von Shopify Scripts sind unter anderem:

  • Rabatte können auch auf Basis von Varianten gegeben werden.

  • Für Gratisprodukte sind beliebig viele Freigrenzen möglich. Genauso können an einer Freigrenze mehrere verschiedene Produkte per "und" oder "oder" verknüpft werden.

  • Mit den "conditional Discounts" wurde eine neue Art von Rabatten eingeführt, mit denen z.B. folgende Fälle abgedeckt werden können:

    • Staffelpreise
    • BOGO (Buy one get one free)
    • Zwei Produkte kaufen, eins gratis erhalten.
    • Ein Produkt kaufen, x % Rabatt auf ein anders Produkt erhalten.
    • Produkt A für 50 € kaufen, 70 % Rabatt auf Produkt B erhalten.
    • 40 % Rabatt auf die ersten drei Einheiten eines Produkts, beginnend mit der vierten Einheit aber nur noch den Standard-Rabatt von 10 %.
    • Vier für drei: Vier Produkte kaufen, das günstigste davon gratis erhalten.

Um nach Eingabe eines Discount-Codes Streichpreise in den Kategorien und an den Produkten anzuzeigen, wurde ein Tool benötigt, das auch die Streichpreise zu den neuen Rabattarten korrekt und zuverlässig ermitteln kann. Dabei muss alles berücksichtigt werden, das auch im Checkout Berücksichtigung findet.

Aufgrund der neuen Komplexität sollte von Anfang an vermieden werden, dass ein Storefront-Entwickler sich in den Bereich Rabattierung einarbeiten muss, wenn er eine Funktionalität schaffen soll, die irgendwie mit Rabatten zu tun hat. Daher wurde beschlossen eine Library zu entwickeln, die von jedem wie eine Blackbox genutzt werden kann. D.h.: Der Entwickler kann die Funktionen der Library aufrufen, ohne dass er sich über die Details der Berechnungen Gedanken machen muss. Dadurch befinden sich alle im Kontext Rabattierung benötigten Funktionen an einer Stelle, und es wird ein Patchwork vermieden, das schwer zu überschauen und zu warten ist.

Konkret wurden die folgenden Funktionen im Zusammenhang mit der Rabattierung benötigt:

  • Setzen, Ändern und Entfernen von Discount-Codes.
  • Berechnung des rabattierten Produktpreises zur Darstellung des Streichpreises.
  • Separate Abfrage der conditional Discounts für ein Produkt.
  • Informationen für den Vergabeprozess von Gratisprodukten:
    • Ermittlung der Freigrenzen.
    • Zu jeder Freigrenze die Info ermitteln, ob verfügbare Produkte existieren.
    • Ermittlung der Produkte, die gratis sein sollen aber noch nicht vergeben wurden.
    • Ermittlung der Gratisprodukte für die jeweils nächste Freigrenze.
    • Ermittlung aller bisher vergebenen Gratisprodukte.
  • Ermittlung des Standard-Discount-Codes (in DE meist "aktion", in NL meist "promo").
  • Ermittlung des Standard-Discounts (Prozentsatz oder Betrag).

Methoden

Die Storefront-Library liefert ein Objekt namens co2 zurück, mit dem die folgenden Methoden zur Verfügung gestellt werden.

getDiscountCode

Zweck:

Ermittlung des aktuell gesetzten Discount-Codes.

Aufrufbeispiel:

await co2.getDiscountCode();

Parameter:

Keine

Rückgabewert:

Discount-Code (string) oder false (boolean). false kommt zurück, falls kein Discount-Code gesetzt ist.

getDiscountType

Zweck:

Ermittlung, ob der aktuell gesetzt Discount-Code ein Influencer-Code ist.

Aufrufbeispiel:

await co2.getDiscountType();

Parameter:

Keine

Rückgabewert:

"infl", "pub" (string) oder false (boolean). Bedeutungen:

  • "infl" => Influencer-Code
  • "pub" => kein Influencer-Code
  • false => kein Code gesetzt

setDiscountCode

Zweck:

Setzen eines Discount-Codes. Z.B. nach Eingabe durch den Benutzer in einem Input-Feld.

Aufrufbeispiel:

await co2.setDiscountCode("aktion");

Parameter:

Discount-Code (string)

Rückgabewert:

true oder false (boolean). Bedeutungen:

  • true => Discount-Code gesetzt
  • false => Discount-Code unbekannt oder ungültig

removeDiscountCode

Zweck:

Entfernen des aktuell gesetzten Discount-Codes.

Aufrufbeispiel:

await co2.removeDiscountCode();

Parameter:

Keine

Rückgabewert:

true (boolean). Bedeutung: Discount-Code erfolgreich entfernt.

standardDiscountCode

Zweck:

Ermittlung des Rabatt-Codes der Hauptkampagne des aktuellen Marktes.

Aufrufbeispiel:

await co2.standardDiscountCode();

Parameter:

Keine

Rückgabewert:

Discount-Code (string) oder false (boolean). false kommt zurück, falls es aktuell keine Hauptkampagne gibt.

itemDiscountFull

Zweck:

Berechnung des Rabattes auf eine Produktvariante zur Anzeige des Streichpreises in den Collections und auf der Produktseite. Dabei finden alle Rabattarten Berücksichtigung.

Aufrufbeispiel:

await co2.itemDiscountFull(
123456789,
987654321,
[
"category-a",
"category-b",
"sale",
"featured",
"tag-1",
"tag-2"
],
2999,
1
);

Parameter:

Es werden die Produkteigenschaften übergeben, die zum Berechnen des Discounts erforderlich sind:

  • product_id (number, erforderlich),
  • variant_id (number, erforderlich),
  • tags (string[], erforderlich),
  • sub_total (number, unrabattierter Preis in Cents, erforderlich),
  • quantity (number, optional, Default: 1),
  • subscription (boolean, Verkauf mit Selling Plan, optional, Default: false),
  • sale_price (boolean, gibt an, dass das Produkt reduziert ist — compareAtPrice > price, optional, Default: false),
  • variant_tags (string[], Varianten-Tags, intern auf Kleinschreibung normalisiert, für den Variant-Tag-Filter, optional, Default: [])

Rückgabewert:

Objekt mit den Informationen zur Darstellung des Streichpreises. Beispiel:

{
"calc": "perc",
"discPerc": 25,
"discAmt": 0,
"discPrice": 2249,
"condPerc": 30
}

Bedeutung der Properties:

  • calc: Kann gleich "perc" (prozentualer Rabatt) oder "amt" (Amount-Rabatt) sein.
  • discPerc: Prozentsatz, falls prozentualer Rabatt, 0 sonst,
  • discAmt: Rabatt in Cents, falls Amount-Rabatt, 0 sonst.
  • discPrice: Rabattierter Preis in Cents.
  • condPerc: Maximal möglicher prozentualer Rabatt über conditional Discounts.

itemDiscount

Zweck:

Berechnung des Rabattes auf eine Produktvariante ohne Berücksichtigung von conditional Discounts. D.h.: Die Property discPrice wird nur auf Basis der Standard-Discount-Gruppen berechnet. Zu den conditional Discounts wird aber der maximal mögliche prozentuale Rabatt zurückgegeben.

Aufrufbeispiel:

await co2.itemDiscount(
123456789,
987654321,
[
"category-a",
"category-b",
"sale",
"featured",
"tag-1",
"tag-2"
],
2999,
1
);

Parameter:

Es werden die Produkteigenschaften übergeben, die zum Berechnen des Discounts erforderlich sind:

  • product_id (number, erforderlich),
  • variant_id (number, erforderlich),
  • tags (string[], erforderlich),
  • sub_total (number, unrabattierter Preis in Cents, erforderlich),
  • quantity (number, optional, Default: 1),
  • subscription (boolean, Verkauf mit Selling Plan, optional, Default: false),
  • sale_price (boolean, gibt an, dass das Produkt reduziert ist — compareAtPrice > price, optional, Default: false),
  • variant_tags (string[], Varianten-Tags, intern auf Kleinschreibung normalisiert, für den Variant-Tag-Filter, optional, Default: [])

Rückgabewert:

Objekt mit den Informationen zur Darstellung des Streichpreises. Wird kein Rabatt gefunden, dann sind discPerc und discAmt gleich 0, und discPrice entspricht dem Parameter sub_total. Beispiel:

{
"calc": "perc",
"discPerc": 20,
"discAmt": 0,
"discPrice": 2399,
"condPerc": 30
}

Bedeutung der Properties:

  • calc: Kann gleich "perc" (prozentualer Rabatt) oder "amt" (Amount-Rabatt) sein.
  • discPerc: Prozentsatz, falls prozentualer Rabatt, 0 sonst,
  • discAmt: Rabatt in Cents, falls Amount-Rabatt, 0 sonst.
  • discPrice: Rabattierter Preis in Cents.
  • condPerc: Maximal möglicher prozentualer Rabatt über conditional Discounts.

standardDiscount

Zweck:

Berechnung des Rabattes auf eine Produktvariante mit dem Discount-Code der Hauptkampagne. Dient der Anzeige am Produkt, wenn der Benutzer keinen Discount-Code im Warenkorb hat.

Aufrufbeispiel:

await co2.standardDiscount(
123456789,
987654321,
[
"category-a",
"category-b",
"sale",
"featured",
"tag-1",
"tag-2"
],
2999,
1
);

Parameter:

Es werden die Produkteigenschaften übergeben, die zum Berechnen des Discounts erforderlich sind:

  • product_id (number, erforderlich),
  • variant_id (number, erforderlich),
  • tags (string[], erforderlich),
  • sub_total (number, unrabattierter Preis in Cents, erforderlich),
  • quantity (number, optional, Default: 1),
  • subscription (boolean, Verkauf mit Selling Plan, optional, Default: false),
  • sale_price (boolean, gibt an, dass das Produkt reduziert ist — compareAtPrice > price, optional, Default: false),
  • variant_tags (string[], Varianten-Tags, intern auf Kleinschreibung normalisiert, für den Variant-Tag-Filter, optional, Default: [])

Rückgabewert:

Objekt mit den Informationen zum Rabatt auf ein Produkt bei Anwendung des Standard-Discounts. Wird kein Rabatt gefunden, dann sind discPerc und discAmt gleich 0, und discPrice entspricht dem Parameter sub_total. Beispiel:

{
"calc": "perc",
"discPerc": 20,
"discAmt": 0,
"discPrice": 2399
}

Bedeutung der Properties:

  • calc: Kann gleich "perc" (prozentualer Rabatt) oder "amt" (Amount-Rabatt) sein.
  • discPerc: Prozentsatz, falls prozentualer Rabatt, 0 sonst,
  • discAmt: Rabatt in Cents, falls Amount-Rabatt, 0 sonst.
  • discPrice: Rabattierter Preis in Cents.

conditionalDiscounts

Zweck:

Liefert die conditional Discounts zurück, bei denen sich die Condition Settings oder die Discount Settings (oder beides) auf das Produkt/die Produktvariante beziehen.

Aufrufbeispiel:

await co2.conditionalDiscounts(
123456789,
987654321,
[
"category-a",
"category-b",
"sale",
"featured"
]
);

Parameter:

  • product_id (number, erforderlich)
  • variant_id (number, erforderlich)
  • tags (string[], erforderlich)
  • subscription (boolean, Verkauf mit Selling Plan, optional, Default: false)
  • sale_price (boolean, gibt an, dass das Produkt reduziert ist — compareAtPrice > price, optional, Default: false)
  • variant_tags (string[], Varianten-Tags, intern auf Kleinschreibung normalisiert, für den Variant-Tag-Filter, optional, Default: [])

Rückgabewert:

Liefert ein Objekt mit den Properties rule_applies und condition_applies zurück. Werden keine conditional Discounts gefunden, dann sind die beiden Arrays leer. Bedeutung der Arrays:

  • Ein Eintrag in condition_applies bedeutet, dass die Bedingung auf das Produkt passt.
  • Ein Eintrag in rule_applies bedeutet dagegen, dass der Discount auf das Produkt anwendbar ist.

Im Beispiel ist definiert, dass man einen Rabatt von 50 % auf Produkt A erhält (Produkt-ID 111111111), wenn man Produkt B kauft (Produkt-ID 222222222):

{
"rule_applies": [],
"condition_applies": [
{
"quant": 1,
"count": 0,
"disc_perc": 50,
"base": "prods",
"tags": [],
"ex_tags": [],
"product_items": [
111111111
],
"product_variant_items": [],
"multi_apply": "multiply",
"cond_base": "prods",
"cond_tags": [],
"cond_ex_tags": [],
"cond_product_items": [
222222222
],
"cond_product_variant_items": [],
"cond_minimum": "quant",
"cond_quant": 1,
"cond_threshold": 0,
"cond_count": 1,
"cond_sub_total": 0,
"cond_threshold_used": 0,
"cond_text": ""
}
]
}

Ein Eintrag im Array rule_applies oder condition_applies hat die folgenden Properties. Man beachte dabei, dass alle Felder, die mit "cond_" beginnen, zum Bedingungsteil gehören, während über die anderen Felder der Rabatt definiert wird.

  • quant: Anzahl Produkte, für die der Rabatt gilt (number). Dieser Wert muss zusammen mit dem des Feldes multi_apply betrachtet werden. Ist multi_apply gleich "once", dann wird der Rabatt nur quant mal angewendet. Im Beispiel würde das bedeuten, dass der Kunde nur einmal 50 % auf Produkt A erhält, egal wie viele Einheiten von Produkt B er kauft. Hier ist die Einstellung dagegen "mulitply". D.h.: Kauft der Kunde n mal Produkt B, dann erhält er auch n mal den Rabatt auf Produkt A. Es gibt noch die Einstellung "unlimited", die z.B. bei Staffelrabatten zur Anwendung kommt, bei denen der Rabatt unbegrenzt oft gilt.
  • count: Anzahl der Produkte im Warenkorb, auf die der Rabatt angewendet worden ist (number).
  • disc_perc: Rabatt in % (number).
  • base: Die zu rabattierenden Produkte können durch Angabe von Produkten ("prods"), Varianten ("vars") oder Tags ("tags") definiert werden (string).
  • tags: Array von Tags, falls base == "tags" (string[]).
  • ex_tags: Array von Ausschluss-Tags, falls base == "tags" (string[]).
  • product_items: Array von Produkt-IDs, falls base == "prods" (number[]).
  • product_variant_items: Array von Varianten-Objekten, falls base == "vars". Dabei besitzt jedes Objekt zwei Properties: pid (Produkt-ID) und vids (Varianten-IDs).
  • multi_apply: "once", "multiply" oder "unlimited" (string).
  • cond_base: Bezieht sich auf die Produkte der Bedingung und kann analog zu base gleich "prods", "vars" oder "tags" sein (string).
  • cond_tags: Array von Tags, falls cond_base == "tags" (string[]).
  • cond_ex_tags: Array von Ausschluss-Tags, falls cond_base == "tags" (string[]).
  • cond_product_items: Array von Produkt-IDs, falls cond_base == "prods" (number[]).
  • cond_product_variant_items: Array von Varianten-Objekten, falls cond_base == "vars".
  • cond_minimum: Bringt zum Ausdruck, ob sich die Bedingung auf eine Mindestanzahl ("quant") oder einen Mindestbetrag ("threshold") bezieht (string).
  • cond_quant: Mindestanzahl, falls cond_minimum == "quant" (number). 0 sonst.
  • cond_threshold: Mindestbetrag, falls cond_minimum == "threshold" (number). 0 sonst.
  • cond_count: Anzahl von Produkten im Warenkorb, die die Bedingung erfüllen, falls cond_minimum == "quant" (number). 0 sonst.
  • cond_sub_total: Summe der rabattierten Preise der Produkte, die die Bedingung erfüllen, falls cond_minimum == "threshold" (number). 0 sonst.
  • cond_threshold_used: Falls cond_minimum == "threshold" und die Regel angewendet werden konnte, dann der threshold, der bei Anwendung verwendet wurde (number).
  • cond_text: Info-Text zur Ausgabe am Produkt (string).
  • use_variant_tags: Ob für diese Regel der reward-seitige Variant-Tag-Filter aktiv ist (boolean).
  • variant_tags: Reward-seitige Variant-Tag-Include-Liste, auf Kleinschreibung normalisiert (string[]).
  • ex_variant_tags: Reward-seitige Variant-Tag-Exclude-Liste, auf Kleinschreibung normalisiert (string[]).
  • cond_use_variant_tags: Ob für diese Regel der condition-seitige Variant-Tag-Filter aktiv ist (boolean).
  • cond_variant_tags: Condition-seitige Variant-Tag-Include-Liste, auf Kleinschreibung normalisiert (string[]).
  • cond_ex_variant_tags: Condition-seitige Variant-Tag-Exclude-Liste, auf Kleinschreibung normalisiert (string[]).

freeProductThresholds

Zweck:

Ermittlung der Gratisprodukt-Freigrenzen zur Darstellung im Warenkorb.

Aufrufbeispiel:

await co2.freeProductThresholds();

Parameter:

Keine

Rückgabewert:

Array mit den Gratisprodukt-Freigrenzen in Cents (number[]). Das Array ist leer, falls es keine Gratisprodukte gibt. Beispiel:

[5000, 7500, 10000]

Bedeutung: Freigrenzen in Cents.

thresholdAvailabilities

Zweck:

Ermittlung der Produktverfügbarkeit zu den Gratisprodukt-Freigrenzen. Dient der Darstellung im Warenkorb.

Aufrufbeispiel:

await co2.thresholdAvailabilities();
await co2.thresholdAvailabilities(true);

Parameter:

  • getAll (boolean, optional, Default: false): Steuert den Umfang des Ergebnisses. Bei false werden nur die Freigrenzen zurück gegeben, die durch die aktuelle Warenkorbsumme bereits erreicht sind (bisheriges Verhalten, entspricht dem ersten Aufruf oben). Bei true werden alle konfigurierten Freigrenzen zurück gegeben, unabhängig vom Warenkorbzustand; das eignet sich z.B. für eine Fortschrittsanzeige mit allen Freigrenzen im Warenkorb.

Rückgabewert:

Array von Objekten mit Gratisproduktfreigrenze und Produktverfügbarkeit. Beispiel:

[
{
"thresh": 5000,
"available": true
},
{
"thresh": 7500,
"available": false
},
{
"thresh": 10000,
"available": true
}
]

Bedeutung:

  • thresh: Gratisproduktfreigrenze (number).
  • available: true, wenn der Freigrenze mindestens ein Gratisprodukt bzw. mindestens eine Gratisprodukt-Variante zugeordnet ist, die nicht ausverkauft ist, dem für die Regel konfigurierten sale_mode entspricht und den Variant-Tag-Filter der Regel besteht (boolean). Der sale_mode filtert reduzierte Varianten: "include" (Standardwert) lässt alle Varianten zu, "exclude" blendet reduzierte Varianten aus, "only" lässt nur reduzierte Varianten zu. false bedeutet, dass keine passende Variante übrig bleibt — entweder sind alle Kandidaten ausverkauft oder sie werden durch den sale_mode oder den Variant-Tag-Filter ausgeschlossen.

freeProductKeys

Zweck:

Ermittlung der Item-Keys der Gratisprodukte im Warenkorb.

Aufrufbeispiel:

await co2.freeProductKeys();

Parameter:

Keine

Rückgabewert:

Array mit den Item-Keys der Gratisprodukte (string[]). Das Array ist leer, falls es keine Gratisprodukte gibt. Beispiel:

[
"111111111:abc123def456ghi789",
"222222222:jkl012mno345pqr678",
"333333333:stu901vwx234yz567"
]

Bedeutung: Item-Keys, mit denen die Gratisprodukte im Warenkorb identifiziert werden können.

unredeemedFreeProducts

Zweck:

Ermittlung der Gratisprodukte, die dem Kunden zustehen, sich aber noch nicht im Warenkorb befinden. Ausverkaufte Produkte werden nicht zurück geliefert.

Aufrufbeispiel:

await co2.unredeemedFreeProducts();

Parameter:

Keine

Rückgabewert:

Array mit Gratisproduktobjekten. Das Array ist leer, falls es keine Gratisprodukte gibt. Beispiel:

[
{
"threshold": 5000,
"sub_total": 7553,
"unredeemedQty": 1,
"base": "prods",
"productItems": [
"product-a",
"product-b"
],
"productVariantItems": [],
"addAutomatically": false
},
{
"threshold": 5000,
"sub_total": 7553,
"unredeemedQty": 2,
"base": "vars",
"productItems": [],
"productVariantItems": [
{
"handle": "product-c",
"variant_ids": [
111111111
]
},
{
"handle": "product-d",
"variant_ids": [
222222222,
333333333,
444444444
]
}
],
"addAutomatically": false
},
{
"threshold": 7500,
"sub_total": 7553,
"unredeemedQty": 1,
"base": "prods",
"productItems": [
"product-e"
],
"productVariantItems": [],
"addAutomatically": true
}
]

Bedeutung:

Die Objekte haben jeweils die folgenden Properties:

  • threshold: Freigrenze in Cents (number),
  • sub_total: Aktuelle Warenkorbsumme in Cents (number),
  • unredeemedQty: Menge, die der Kunde erhalten soll (number),
  • base: Kann "prods" (Produkte) oder "vars" (Varianten) sein.
  • Bei "prods" findet man im Array productItems die Handles der alternativ möglichen Produkte.
  • Bei "vars" ist das Array productVariantItems relevant. Es enthält dann je alternativ möglichem Produkt ein Objekt mit "handle" (Produkt-Handle) und "variant_ids" (erlaubte Varianten-IDs).
  • addAutomatically: Ob das Produkt automatisch zum Warenkorb hinzugefügt werden soll (boolean).

Wenn eine Gratisprodukt-Regel base: "prods" konfiguriert hat, aber ein aktiver Variant-Tag-Filter (use_variant_tags: true mit nicht-leerer Include- oder Exclude-Liste) gilt, oder ihr sale_mode einen anderen Wert als "include" hat, gibt die Library die Regel als base: "vars" zurück und füllt productVariantItems mit { handle, variant_ids }-Einträgen, die auf die qualifizierenden, nicht ausverkauften Varianten beschränkt sind. Die Konvertierung erfolgt nur zur Laufzeit — die Regel bleibt in der Händler-Konfiguration "prods".

unredeemedCondDiscountProducts

Zweck:

Ermittlung der Produkte, die dem Kunden aktuell per conditional Discount als Upsell angeboten werden. Für jedes Kandidaten-Produkt prüft die Library, ob das Hinzufügen einer Einheit zum Warenkorb einen conditional Discount auslösen würde, dessen Konfiguration auf konkrete Produkte abzielt (Regel-Basis "prods" mit sale_mode: "include"). Dient der Darstellung von Upsells im Warenkorb.

Aufrufbeispiel:

await co2.unredeemedCondDiscountProducts();

Parameter:

Keine

Rückgabewert:

Array mit den Upsell-Produktobjekten. Das Array ist leer, falls aktuell kein Produkt qualifiziert. Beispiel (gekürzt):

[
{
"handle": "sample-product",
"title": "Sample Product",
"image": "https://cdn.example.com/image.png",
"tags": ["tag-a", "tag-b"],
"condPerc": 30,
"variants": [
{
"id": 987654321,
"title": "Default Title",
"price": 1990,
"compareAtPrice": 2490,
"image": "https://cdn.example.com/image.png"
}
]
}
]

Bedeutung:

  • handle: Produkt-Handle (string).
  • title: Produkt-Titel (string).
  • image: URL des Produktbildes (string).
  • tags: Produkt-Tags (string[]).
  • condPerc: Prozentualer Rabatt, der greifen würde, wenn der Kunde das Produkt zum Warenkorb hinzufügt (number).
  • variants: Array von Variantenobjekten, je ein Eintrag pro verfügbarer Variante. Jeder Eintrag hat die folgenden Properties:
    • id: Varianten-ID (number).
    • title: Varianten-Titel (string).
    • price: Variantenpreis in Cents (number).
    • compareAtPrice: Compare-at-Preis der Variante in Cents, bzw. 0, falls kein Compare-at-Preis gesetzt ist (number).
    • image: URL des Variantenbildes (string).

Beachten: Die Variantenstruktur weicht hier von der bei freeProductData zurückgegebenen ab — diese Methode liefert zusätzlich compareAtPrice, da conditional-Discount-Upsells häufig davon abhängen, ob eine Variante reduziert ist.

Wenn die selektierte Regel einen aktiven Variant-Tag-Filter hat (use_variant_tags: true mit nicht-leerer Include- oder Exclude-Liste), ist das variants-Array bereits auf die Varianten gefiltert, die den Variant-Tag-Check tatsächlich bestehen. Durch den Filter ausgeschlossene Varianten erscheinen nicht im Ergebnis.

nextFreeProducts

Zweck:

Ermittlung der Gratisprodukte, die der Kunde bei der nächsten, noch nicht erreichten Freigrenze erhält. Freigrenzen, für die alle zugeordneten Gratisprodukte ausverkauft sind, werden dabei übersprungen, damit dem Kunden keine Freigrenze angekündigt wird, die er wegen ausverkaufter Gratisprodukte nicht einlösen kann.

Aufrufbeispiel:

await co2.nextFreeProducts();

Parameter:

Keine

Rückgabewert:

Objekt mit den Informationen zur nächsten, noch nicht erreichten Freigrenze, für die mindestens ein zugeordnetes Gratisprodukt nicht ausverkauft ist. Falls es keine solche Freigrenze (mehr) gibt, wird false zurück gegeben. Beispiel:

{
"threshold": 10000,
"sub_total": 7553,
"items": [
{
"threshold": 10000,
"sub_total": 7553,
"unredeemedQty": 1,
"base": "prods",
"productItems": [
"product-f"
],
"productVariantItems": [],
"addAutomatically": true
}
]
}

Bedeutung:

Die Objekt-Properties haben die folgende Bedeutung:

  • threshold: Nächste Freigrenze in Cents (number),
  • sub_total: Aktuelle Warenkorbsumme in Cents (number),
  • items: Array mit Gratisproduktobjekten. Die Struktur ist identisch zum Array, das von unredeemedFreeProducts zurückgegeben wird.

Es gilt die unter unredeemedFreeProducts beschriebene prods-zu-vars-Laufzeit-Konvertierung: Die von nextFreeProducts zurückgegebenen Items folgen den gleichen Shape-Regeln, einschließlich der Fälle mit aktivem Variant-Tag-Filter und nicht-"include"-sale_mode.

freeProductData

Zweck:

Ermittlung der Daten aller Produkte, die für die aktuelle Kampagne als Gratisprodukte definiert sind. Dabei werden je Produkt alle verfügbaren Varianten zurück geliefert,

Aufrufbeispiel:

await co2.freeProductData();

Parameter:

Keine

Rückgabewert:

Array mit Gratisprodukten. Das Array ist leer, falls es keine Gratisprodukte gibt. Beispiel (gekürzt):

[
{
"id": 123456789,
"title": "Sample Product",
"handle": "sample-product",
"productType": "Product Type",
"image": "https://cdn.example.com/image.png",
"variants": [
{
"id": 987654321,
"title": "Default Title",
"price": 1990,
"image": "https://cdn.example.com/image.png"
}
]
}
]

Bedeutung:

Die Produkt-Objekte haben jeweils die folgenden Properties:

  • id: Produkt-ID (number)
  • title: Produkt-Titel (string)
  • handle: Produkt-Handle (string)
  • productType: Produkt-Typ (string)
  • image: Url Featured Image (string)
  • variants: Array mit allen verfügbaren Variantenobjekten. Properties sind jeweils:
    • id: Varianten-ID (number)
    • title: Varianten-Titel (string)
    • price: Variantenpreis in Cents (number)
    • image: Url Variantenbild (string)

Integration

Storefront API Access Token

Um funktionieren zu können, muss die Library Discount-Codes lesen und setzen, die Produkt-Tags der Produkte im Warenkorb und die Produkteigenschaften von Gratisprodukten ermitteln können. Dafür wird ein Storefront API Access Token für Produktdaten benötigt. Warenkorbstatus und Discount-Code-Änderungen laufen über Shopifys Cart-Endpunkte.

Das Token wird direkt über die App erzeugt. Die Token-Erzeugung ist im Ultimate-Plan verfügbar; Details siehe Pricing-Seite der Datora-App.

  1. Im Menü der App auf Settings klicken.
  2. Im Bereich Storefront Access Token auf Generate Token klicken.
  3. Es wird ein Zugang erzeugt, und das Token kann kopiert werden.

Anschließend den Theme-Editor aufrufen über:

Online Store > Themes > Customize

In der Leiste links auf das dritte Symbol für App embeds klicken. Die beiden folgenden Schalter müssen aktiviert sein bzw. werden:

  • Datora | Multi Discounts
  • Storefront API Client

Bei Storefront API Client auf den kleinen Pfeil klicken, und es wird das Feld Storefront API access token sichtbar. Hier das Token eintragen und speichern.

DATORA Sales Channel

Nachdem das Storefront API Token erzeugt und im Theme eingetragen wurde, muss der DATORA Sales Channel den Produkten zugeordnet werden. Das ist erforderlich, wenn die Storefront Library für Gratisprodukte oder Conditional Discounts genutzt wird.

Im Shopify-Admin: die Produktliste öffnen und alle Produkte auswählen, auf das Label "N selected" klicken, dann Select all in this store > ... > Include in sales channels > DATORA > Include products.

Events

cart.update

Die Storefront Library verwaltet einen State zum Warenkorb und hört dabei auf das Event cart.update. Immer dann, wenn eine Änderung am Warenkorb erfolgt, muss dieses Event ausgelöst werden. Daraufhin aktualisiert die Storefront Library ihren State. Das Auslösen des Events kann wie folgt gemacht werden:

window.dispatchEvent(new CustomEvent('cart.update'));

Für den Einbau muss, abhängig vom verwendeten Theme, eine geeignete Stelle gesucht werden. Das folgende Snippet ist ein Dawn-spezifisches Beispiel, keine theme-agnostische API: Im Theme Dawn kann der folgende Code an das Ende der Datei global.js gesetzt werden:

subscribe(PUB_SUB_EVENTS.cartUpdate, (event) => {
window.dispatchEvent(new CustomEvent('cart.update', {
detail: { cart: event.cartData }
}));
});

In diesem Beispiel wird auch der Cart übergeben. Das ist nicht notwendig, jedoch macht es das Programm performanter, weil die Storefront Library sonst den Cart noch einmal selbst lesen muss. Wenn er also zur Verfügung steht, dann sollte er übergeben werden.

co2.afterCartAttributeChange

Wenn ein Benutzer einen Code-Link verwendet, setzt die Storefront Library ein Cart Attribute neu und sendet das Window-Event co2.afterCartAttributeChange. Damit der neue Code sofort angewandt wird, muss der Warenkorb aktualisiert werden. Dazu ist es in Dawn nötig, ein cartUpdate-Event zu publishen.

Im Theme Dawn (ebenfalls als theme-spezifisches Beispiel) kann der folgende Code ebenfalls an das Ende der Datei global.js gesetzt werden:

window.addEventListener('co2.afterCartAttributeChange', () => {
publish(PUB_SUB_EVENTS.cartUpdate, { source: 'global', cartData: null });
});

Dadurch wird nach dem Setzen des Cart Attributes automatisch ein Warenkorb-Update ausgelöst, und der neue Discount-Code wird sofort wirksam.

co2.cart.initialized

Die Storefront Library feuert das Window-Event co2.cart.initialized, sobald sie ihre Initialisierung abgeschlossen hat. Dies geschieht sowohl beim ersten Laden der Seite als auch nach jeder Warenkorb-Änderung — also immer dann, wenn die Library ihren internen State neu initialisiert hat.

Damit ist dieses Event der empfohlene Aufhänger zum Aktualisieren der Streichpreise: Auf co2.cart.initialized hören, wenn die Preise nach dem Laden der Seite oder einer Warenkorb-Änderung neu berechnet werden sollen. Man muss keine eigene Logik (z. B. rund um DOMContentLoaded) bauen, um den passenden Zeitpunkt für die Abfrage zu ermitteln — das Event signalisiert genau diesen Moment, und der interne State der Library ist dann garantiert aktuell.

Beispiel:

window.addEventListener('co2.cart.initialized', async () => {
const discount = await co2.itemDiscount(
productId, variantId, tags, subTotal, quantity, false, false
);
// Streichpreis aktualisieren ...
});

Sale-Price-Logik

  • Eine Variante gilt als Sale-Price-Variante, wenn compareAtPrice > price.
  • Die Storefront-Library leitet dies bei den Wrapper-Aufrufen nicht selbst ab; der Theme-Aufrufer übergibt sale_price (boolean) an itemDiscount, itemDiscountFull, standardDiscount und conditionalDiscounts.
  • Das sale_mode einer Discount-Regel steuert, wie Sale-Price-Varianten behandelt werden: "include" (Standardwert) lässt alle Varianten zu, "exclude" blendet reduzierte Varianten aus, "only" lässt nur reduzierte Varianten zu.
  • Diese sale_mode-Filterung wird verwendet, wenn die Storefront-Library prüft, ob eine Discount-Regel auf eine Variante passt. Sie zeigt sich in der Gratisprodukt-Verfügbarkeit und -Vorschau (thresholdAvailabilities, unredeemedFreeProducts, nextFreeProducts) sowie in den conditional-Discount-Prüfungen. unredeemedCondDiscountProducts liefert nur Upsell-Produkte für base: "prods"-Regeln mit sale_mode: "include" und verwendet beim Prüfen der Kandidaten-Varianten intern compareAtPrice > price.
  • Bestehende Beispiele bleiben unverändert: sale_price ist optional und hat den Standardwert false, sodass Aufrufer, die es weglassen, weiterhin funktionieren.

Einrichtung des Variant-Tag-Filters

Metafield-Definition für co2.variant_tags

Varianten-Tags werden in einem Shopify-Metafield abgelegt. Die Definition wird einmal pro Shop angelegt:

  1. Im Shopify-Admin EinstellungenBenutzerdefinierte DatenVariantenDefinition hinzufügen öffnen.
  2. Namespace und Key auf co2 bzw. variant_tags setzen.
  3. Als Typ Liste mit einzeiligem Text wählen (json funktioniert ebenfalls; die Library verarbeitet beide Formen).
  4. Unter Zugriff den Schalter Storefront API: lesen aktivieren. Ohne diesen Zugriff fällt die Storefront-Vorschau auf "keine Varianten-Tags" zurück; Cart und Checkout greifen weiterhin korrekt, weil die Shopify Function Metafields serverseitig liest.

Tags an Varianten vergeben

Eine Produkt-Variante öffnen und das Feld co2.variant_tags mit den für die Filterung relevanten Tags füllen (zum Beispiel ["exclusive", "vip_member"]). Tags werden vor dem Vergleich auf Kleinschreibung normalisiert.

ASCII-Slug-Werte wie exclusive, vip_member oder size-xl verwenden. Für ASCII-Tags ist case-insensitive Matching garantiert; daher Umlaute oder locale-spezifische Groß-/Kleinschreibung vermeiden.

Conditional-Discount-Regel konfigurieren

Im Admin der Datora-App eine Conditional-Discount-Regel anlegen oder bearbeiten und Filter by Variant-Tags aktivieren. Je nach Bedarf die Include- und/oder Exclude-Listen auf der Reward- bzw. Condition-Seite festlegen.

Anpassung im Theme (optional, aber empfohlen)

Für eine korrekte Storefront-Vorschau (Streichpreise, Badges) auf variant-tag-gefilterten Produkten wird das Theme so angepasst, dass die Varianten-Tags als letztes positionelles Argument an die jeweilige Storefront-Library-Methode übergeben werden. Das Argument muss ein einfaches string[] sein. Shopify gibt list-of-text-Metafield-Werte im Storefront als JSON-serialisierten String aus, daher zuerst parsen:

let variantTags = [];
const rawVT = variant.metafields?.co2?.variant_tags?.value;
if (rawVT) {
try { variantTags = JSON.parse(rawVT); } catch { variantTags = []; }
if (!Array.isArray(variantTags)) variantTags = [];
}

await co2.itemDiscountFull(
product.id,
variant.id,
product.tags,
variant.price,
1,
false, // subscription
variant.compareAtPrice > variant.price, // sale_price
variantTags // variant_tags
);

Ohne diese Anpassung fällt die Library für die Vorschau-Berechnungen auf ein leeres Variant-Tag-Set zurück. Cart und Checkout sind davon nicht betroffen, weil die Shopify Function Varianten-Metafields direkt serverseitig liest.