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-Codefalse=> 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 gesetztfalse=> 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_appliesbedeutet, dass die Bedingung auf das Produkt passt. - Ein Eintrag in
rule_appliesbedeutet 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 Feldesmulti_applybetrachtet werden. Istmulti_applygleich "once", dann wird der Rabatt nurquantmal 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, fallsbase == "tags"(string[]).ex_tags: Array von Ausschluss-Tags, fallsbase == "tags"(string[]).product_items: Array von Produkt-IDs, fallsbase == "prods"(number[]).product_variant_items: Array von Varianten-Objekten, fallsbase == "vars". Dabei besitzt jedes Objekt zwei Properties:pid(Produkt-ID) undvids(Varianten-IDs).multi_apply: "once", "multiply" oder "unlimited" (string).cond_base: Bezieht sich auf die Produkte der Bedingung und kann analog zubasegleich "prods", "vars" oder "tags" sein (string).cond_tags: Array von Tags, fallscond_base == "tags"(string[]).cond_ex_tags: Array von Ausschluss-Tags, fallscond_base == "tags"(string[]).cond_product_items: Array von Produkt-IDs, fallscond_base == "prods"(number[]).cond_product_variant_items: Array von Varianten-Objekten, fallscond_base == "vars".cond_minimum: Bringt zum Ausdruck, ob sich die Bedingung auf eine Mindestanzahl ("quant") oder einen Mindestbetrag ("threshold") bezieht (string).cond_quant: Mindestanzahl, fallscond_minimum == "quant"(number). 0 sonst.cond_threshold: Mindestbetrag, fallscond_minimum == "threshold"(number). 0 sonst.cond_count: Anzahl von Produkten im Warenkorb, die die Bedingung erfüllen, fallscond_minimum == "quant"(number). 0 sonst.cond_sub_total: Summe der rabattierten Preise der Produkte, die die Bedingung erfüllen, fallscond_minimum == "threshold"(number). 0 sonst.cond_threshold_used: Fallscond_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. Beifalsewerden nur die Freigrenzen zurück gegeben, die durch die aktuelle Warenkorbsumme bereits erreicht sind (bisheriges Verhalten, entspricht dem ersten Aufruf oben). Beitruewerden 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 konfiguriertensale_modeentspricht und den Variant-Tag-Filter der Regel besteht (boolean). Dersale_modefiltert reduzierte Varianten:"include"(Standardwert) lässt alle Varianten zu,"exclude"blendet reduzierte Varianten aus,"only"lässt nur reduzierte Varianten zu.falsebedeutet, dass keine passende Variante übrig bleibt — entweder sind alle Kandidaten ausverkauft oder sie werden durch densale_modeoder 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
productItemsdie Handles der alternativ möglichen Produkte. - Bei "vars" ist das Array
productVariantItemsrelevant. 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 vonunredeemedFreeProductszurü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.
- Im Menü der App auf Settings klicken.
- Im Bereich Storefront Access Token auf Generate Token klicken.
- 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) anitemDiscount,itemDiscountFull,standardDiscountundconditionalDiscounts. - Das
sale_modeeiner 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.unredeemedCondDiscountProductsliefert nur Upsell-Produkte fürbase: "prods"-Regeln mitsale_mode: "include"und verwendet beim Prüfen der Kandidaten-Varianten interncompareAtPrice > price. - Bestehende Beispiele bleiben unverändert:
sale_priceist optional und hat den Standardwertfalse, 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:
- Im Shopify-Admin Einstellungen → Benutzerdefinierte Daten → Varianten → Definition hinzufügen öffnen.
- Namespace und Key auf
co2bzw.variant_tagssetzen. - Als Typ Liste mit einzeiligem Text wählen (
jsonfunktioniert ebenfalls; die Library verarbeitet beide Formen). - 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.