+420 728 089 029EN

Časté problémy s balíčkem a jejich řešení

8 min čtení

Většina hlášení typu „doplněk Shoptet nefunguje" má jednu z několika opakujících se příčin. Sesbírali jsme je z reálných nasazení a seřadili podle toho, jak často je vidíme.

Než začnete hledat v tomhle seznamu, projděte tři kroky. Odfiltrují většinu případů za minutu:

  1. DevTools → Network, filtr multipack. Načetly se multipack.js a multipack.css se statusem 200? Proběhlo načtení konfigurace a jaký vrátilo status?
  2. DevTools → Console. Je tam Uncaught? Chyba v renderu vypadá zvenku úplně stejně jako „skript se nenačetl".
  3. Podívejte se na skutečnou URL requestu. Neobsahuje /null nebo /undefined? Je v ní shortcode bez hranatých závorek?

Příklady vycházejí ze smyšleného e-shopu www.example.cz (projectId 123456) a konfigurace „Kávový balíček" se shortcodem w-multipack-ABC12 — 3 sloty, sleva 15 %.

Widget se nevykreslí

1. Na stránce není vůbec nic

Skript se buď nenačetl, nebo se načetl a spadl při renderu. V Network musí multipack.js vrátit 200 — status 404 znamená překlep v cestě /user/documents/… nebo soubor nahraný o adresář jinam. Žádný request znamená, že HTML vložka není uložená, nebo je uložená jen pro jinou jazykovou mutaci či doménu e-shopu.

Když request projde a přesto se nic nevykreslí, otevřete konzoli. Typické pády jsou na null v poli url nebo default_category a na skladovosti zpracované jako číslo — stock chodí jako string "12.000", takže potřebuje parseFloat.

2. Na stránce je vidět holý text shortcodu

Skript nenašel shortcode v podobě, kterou hledá — buď se vůbec nenačetl (viz bod výš), nebo se text shortcodu ve stránce liší od očekávaného tvaru. Rychlá kontrola: Ctrl+U a vyhledat w-multipack ve zdrojovém kódu stránky.

3. WYSIWYG editor shortcode rozbil

Nejčastější příčina předchozího bodu. Editor obsahu si text rád „opraví" — a rozdělený shortcode už neodpovídá vzoru, který skript hledá.

Co v HTML uvidíte Co se stalo
[w-multipack-ABC12] editor převedl hranaté závorky na entity
[w-multipack-<span>ABC12</span>] text se rozpadl na víc uzlů, typicky po vložení z Wordu
[w-multipack-ABC12] s &nbsp; uvnitř nezlomitelná mezera z formátování
[w-multipack-abc12] malá písmena — shortcode je case-sensitive, tvar je [A-Z0-9]{5}

Spolehlivé řešení je vždycky stejné: v editoru přepnout do režimu zdrojového kódu, shortcode smazat a napsat ručně jako prostý text na samostatný řádek. Pak uložit a zkontrolovat přes Ctrl+U.

4. Změna JS nebo CSS se na e-shopu neprojevila

Soubory z /user/documents/ jdou přes CDN a browser cache s dlouhým TTL, takže samotné nahrání nové verze nestačí. Deploy je dvoukrokový: nahrát soubor a zvýšit parametr ?v= v HTML vložce.

<link rel="stylesheet" href="/user/documents/multipack/multipack.css?v=1.0.3">
<script src="/user/documents/multipack/multipack.js?v=1.0.3" defer></script>

Verze zvyšujte u obou souborů společně. Rozjeté verze (CSS 1.02, JS 1.07) jsou klasický zdroj hlášky „u mě to vypadá jinak než u tebe".

Chyby z API

5. Načtení konfigurace vrací 404

Pro dvojici projectId + shortcode neexistuje konfigurace. Zkontrolujte tři věci: že je v URL shortcode bez hranatých závorek, že testujete na e-shopu, kde je konfigurace opravdu založená, a že ji obchodník mezitím nesmazal — smazáním konfigurace shortcode přestává platit.

Drobnost, na kterou se hodí myslet dopředu: tělo téhle odpovědi je prostý JSON řetězec, ne objekt. Ve svém error handleru s tím počítejte, ať vám hláška pro zákazníka nevyjde jako undefined.

6. Vytvoření sady vrací 422

Odeslaný výběr neprošel validací. Prakticky vždy jde o jednu ze tří věcí: počet GUIDů neodpovídá product_count (musí jich být přesně tolik), shortcode nemá 17 znaků, nebo některý GUID nemá 36 znaků.

Řešení je preventivní — odesílejte až po kompletním výběru a do té doby držte tlačítko disabled. Tělo 422 je objekt s klíči guids a shortcode; hlášky jsou anglicky a nejsou určené koncovému zákazníkovi, takže podle klíče zobrazte vlastní českou hlášku.

7. V URL requestu se objevuje /null

Cenová hladina je volitelný segment cesty. Naivní skládání URL ho vyrobí i tehdy, když žádná hladina není:

// špatně – v URL vznikne "/null"
const url = `${base}/configuration-products/${shortcode}/${ctx.customer?.priceListId ?? null}`;

// správně – segment se prostě vynechá
const id = Number.isInteger(ctx.customer?.priceListId) ? ctx.customer.priceListId : null;
const url = `${base}/configuration-products/${shortcode}` + (id !== null ? `/${id}` : '');

Pozor při vývoji: na některých e-shopech vrací dataLayer priceListId i nepřihlášeným návštěvníkům, takže se chyba na testovacím účtu vůbec nemusí projevit.

Ceny a měny

8. Cena v košíku se liší od ceny ve widgetu

Widget zobrazuje vlastní orientační výpočet z listingových cen, ale závazná je cena spočítaná backendem. Rozejít se můžou hlavně u produktů s variantami a u akčních cen — listing pracuje s nejnižší cenou napříč variantami, sada se počítá z ceny první varianty v základní měně e-shopu.

Řešení: po úspěšném vytvoření sady přepište zobrazenou cenu hodnotou price z odpovědi a během výběru cenu označte jako orientační. Produkty, které bývají v akci, do balíčku raději nezařazujte — nebo si to předem vyjasněte s obchodníkem.

9. Přihlášený B2B zákazník vidí jiné ceny

Cenová hladina ovlivňuje ceny ve výpisu produktů, ze kterého zákazník vybírá. Sada se pak zakládá z běžných cen se slevou nastavenou v konfiguraci.

Na e-shopech s aktivními cenovými hladinami to řešte domluvou s obchodníkem — balíček se pro B2B segment buď nenabízí, nebo se sleva nastaví tak, aby dávala smysl i pro něj. Ve widgetu pak vždy zobrazte závaznou cenu z odpovědi.

10. E-shop s víc měnami ukazuje částku v korunách

Závazná cena sady je vždy v základní měně e-shopu, zatímco listing je přepočtený kurzem podle měny, kterou má zákazník přepnutou. Zákazníkovi v eurech tedy nesmíte tuhle hodnotu zobrazit jako částku v jeho měně.

Máte dvě varianty: buď ji přepočítat kurzem z dataLayeru a označit jako orientační, nebo cenu ve widgetu nepřepisovat a spolehnout se na přepočet v košíku Shoptetu. Starter na to má přepínač useApiPriceInSummary. A nehardcodujte cenový klíč:

const priceKey = 'price_' + (ctx.currency || 'CZK');   // ne: product.price_CZK
const price = product[priceKey];

11. Produkt se zobrazuje za 0 Kč

Produkt není zařazený v cenové hladině přihlášeného zákazníka, takže k němu nepřichází cena. Nulu nikdy nezobrazujte jako platnou cenu a nepouštějte ji do součtu — produkt v nabídce buď skryjte, nebo místo ceny napište „cena na dotaz".

Košík a odesílání

12. Dvojklik založí dvě sady

Chybí ochrana proti dvojímu odeslání. Každé odeslání zakládá v katalogu reálný produkt, takže dvojklik znamená dva produkty — a když se navíc mezi kliknutími změní pořadí GUIDů, vyhodnotí se to jako jiná kombinace.

Potřebujete tři věci najednou: single-flight guard (druhý klik dostane tentýž Promise), disabled na tlačítku a viditelný spinner. Samotné disabled nestačí, protože mezi kliknutím a nastavením atributu je několik milisekund. A GUIDy vždycky řaďte:

const guids = selection.map((p) => p.guid).slice().sort();

.slice() tam nechte — sort() mutuje pole in-place a rozhodil by vám pořadí slotů v UI.

13. shoptet is not defined

Váš skript běží dřív než šablonové skripty Shoptetu. Typicky proto, že je vložený do hlavičky nebo bez atributu defer.

Skript patří do patičkové HTML vložky s defer, init navažte na DOMContentLoaded a dostupnost si před voláním ověřte:

if (typeof shoptet !== 'undefined' && shoptet.cartShared?.addToCart) {
    shoptet.cartShared.addToCart({ productCode: data.code, amount: quantity });
}

Když funkce chybí, zobrazte srozumitelnou chybu místo tichého selhání. Vlastním fetchem na košíkové akce to neobcházejte — blokuje ho CSRF ochrana Shoptetu.

14. Sada zmizela z katalogu a kód přestal platit

Tohle není chyba, ale záměrné chování. Vygenerované sady jsou produkty vytvořené na míru jednomu nákupu. Každou noc proto běží úklid, který smaže každou sadu, ke které neexistuje objednávka — jinak by e-shopu za pár měsíců zaplevelily katalog stovky jednorázových produktů.

Prakticky to znamená, že kód sady platí nejdéle do nejbližší půlnoci. Neukládejte ho do localStorage, do sdílených odkazů ani do e-mailů. Ukládejte si GUIDy vybraných produktů a při návratu zákazníka si o sadu požádejte znovu — stejná kombinace vrátí existující sadu, jinak se založí nová. Objednané sady zůstávají.

Vzhled a UX

15. Obrázky produktů se nenačítají

Pole image není URL, ale název souboru včetně podepsaného query stringu. Nejčastější chyba je, že se query string odstraní nebo znovu zaenkóduje, případně se použije hostname stránky místo hostname z product.url.

const host = product.url ? new URL(product.url).hostname : window.location.hostname;
const src  = `https://cdn.myshoptet.com/usr/${host}/user/shop/orig/${product.image}`;

Query string nechte beze změny a počítejte s tím, že image může chybět — mějte připravený placeholder.

16. Widget rozbil layout šablony

Globální CSS. Pravidla na holých selektorech (h2, select, .rating-star, :root) se propíšou do celého e-shopu, protože widget běží uvnitř cizí šablony.

Všechna pravidla zanořte pod kořenovou třídu widgetu (.wt-multipack …), včetně věcí jako scroll-behavior nebo max-width na kontejnerech. Obrázkům nastavte width/height nebo aspect-ratio, ať vám při načítání neskáče layout.

17. Vyprodaný produkt se dostane do sady

Hlídat, co je v nabídce, je úkol frontendu — a je to jedna z věcí, které stojí za pět minut práce navíc. Vyprodané produkty ve výběru zablokujte nebo skryjte a nezapomeňte, že stock je string:

const available = parseFloat(product.stock) > 0;

Finální kontrola dostupnosti pak proběhne v košíku Shoptetu podle nastavení e-shopu, ale zákazník by se do ní neměl dostat překvapením.

18. Počet slotů nesedí nebo se sloty přehazují

Počet slotů je odvozený z column_number místo z product_count, nebo se kód spoléhá na pořadí, ve kterém kategorie dorazí.

Autoritativní je vždy product_count (2–5). Kategorie si seřaďte sami — podle column_number, pak podle name s localeCompare(…, 'cs'). Totéž platí pro pořadí produktů uvnitř kategorie.

Kdy nám napsat

Když nic z výše uvedeného nepomůže, napište na jsme@webotvurci.cz. Ať to nemusíme řešit na dvakrát, přiložte:

  • URL stránky s widgetem a projectId i shortcode konfigurace,
  • screenshot konzole a záložky Network — ideálně s viditelnou URL requestu, status kódem a tělem odpovědi,
  • čas i s časovou zónou, kdy se to stalo,
  • co jste už zkoušeli a jestli se to děje pořád, nebo jen občas.

Bez projectId a shortcodu se problém na naší straně nedohledá.

Kam dál

Potřebujete s nasazením pomoct nebo si nechat zkontrolovat hotovou implementaci? Ozvěte se na jsme@webotvurci.cz.