„Na liście kurtka kosztuje 569,99 zł, a prawie wszystkie rozmiary są po 499,99.” „Na listingu produkt ma 1,99 zł, na karcie 599,99 zł, klienci myślą, że to błąd cenowy.” „Panel pokazuje 0 sztuk, a rozmiary są na stanie.” W sklepach, które sprawdzałem, wszystkie trzy wynikały z tego samego rekordu magazynowego.
Cena przed wyborem rozmiaru różni się od ceny po wyborze rozmiaru. Dotyczy produktów z wariantami zasilanych importem z BaseLinkera.
Zakładka wariantów w panelu albo jedno zapytanie do Web API o rekordy product-stocks. Porównaj rekord domyślny z rekordami rozmiarów w czterech polach: extended, rozmiar, ean, stock.
Jak Shoper wybiera cenę przed wyborem rozmiaru
Produkt z rozmiarami ma w Shoperze kilka rekordów magazynowych, po jednym na wariant. Jeden z nich nosi flagę wariantu domyślnego. Dokumentacja Shopera opisuje go jako „wyświetlany jako pierwszy na karcie produktu i reprezentujący produkt na listach”. W sklepach, które sprawdzałem, to on decydował o cenie i dostępności widocznych przed kliknięciem rozmiaru: na listingu, w wyszukiwarce, w danych strukturalnych karty i w pliku produktowym dla Google.
Dopóki flaga stoi na dostępnym rozmiarze, wszystko gra. Problem zaczyna się, gdy stoi na rekordzie, którego nie da się kupić.
Wiersz zbiorczy, czyli rekord-widmo
W sklepach, które przeszły z produktów prostych na warianty, albo w których import dokłada rozmiary do istniejących produktów, często zostaje stary rekord sprzed wariantów. Nazywam go wierszem zbiorczym. Poznasz go po czterech cechach:
Pole w product-stocks | Wiersz zbiorczy | Realny rozmiar |
|---|---|---|
extended | 0 | 1 |
| rozmiar (wartość opcji) | brak | np. M, 42, D38 |
ean | zwykle pusty | numer EAN |
stock | suma stanów rozmiarów albo 0 | stan tego rozmiaru |
Rozstrzygają dwie pierwsze cechy: extended: 0 i brak rozmiaru. Pusty EAN i stan równy sumie to wskazówki, nie dowód, bo w niektórych sklepach EAN-y są puste także na realnych rozmiarach.
Import z BaseLinkera dokłada rozmiary jako extended: 1, ale nie ustawia flagi default na żadnym z nich. W sklepie, który badałem najdłużej, po ręcznym przestawieniu flag na rozmiary wracały one na wiersze zbiorcze w ciągu kolejnych tygodni, w miarę jak import dotykał produktów. Czy import nadpisuje flagę, czy odtwarza rekordy, nie potwierdziłem; skutek jest ten sam. Cena wiersza zbiorczego to zwykle cena najdroższego rozmiaru. Bywa też odwrotnie: w drugim sklepie wiersz zbiorczy miał cenę 1,99 zł przy rozmiarach za 599,99 zł. Mechanizm ten sam, objaw inny.
Przykład z katalogu odzieżowego: kurtka pokazywała na liście 569,99 zł, choć siedem z dziewięciu rozmiarów kosztowało 499,99 zł. Te 569,99 to była cena wyłącznie rozmiaru 5XL, dostępnego w jednej sztuce. Cena na liście była więc o 70 zł wyższa od ceny najtańszego dostępnego rozmiaru, co mogło zniechęcać do wejścia w produkt. Skala w tym sklepie, zmierzona 16 i 17 września 2026 roku na 20 653 aktywnych z 22 772 produktów w katalogu: 103 produkty z ceną wyższą od najtańszego dostępnego rozmiaru, średnio o około 40 zł, maksymalnie o 100 zł.
Diagnoza: najpierw panel, potem API
W panelu. Otwórz edycję podejrzanego produktu i zakładkę z wariantami. Zobaczysz listę kombinacji z ceną, stanem i oznaczeniem wariantu domyślnego. Jeśli domyślny nie ma przypisanego rozmiaru, a jego stan albo cena odstają od rozmiarów, masz ten przypadek. To wystarczy, żeby potwierdzić problem na kilku produktach.
Przez API. Dla produktu pobierz listę rekordów magazynowych: GET /webapi/rest/product-stocks?filters={"product_id":12345}. Potem porównaj cztery pola z tabeli powyżej. Jeśli rekord z default: 1 ma extended: 0 i brak rozmiaru, masz odpowiedź.
Przykład z drugiego sklepu, bluza dostępna w jednym rozmiarze:
| Rekord | Cena | Stan | EAN | Rozmiar | Domyślny |
|---|---|---|---|---|---|
| A | 1,99 zł | 0 | pusty | brak | tak |
| B | 599,99 zł | 3 | jest | D38 | nie |
Listing czyta rekord A. Karta po wyborze rozmiaru czyta rekord B. Klient kupuje B i płaci 599,99 zł, więc sprzedaż się zgadza, a zaufanie do sklepu nie.
Jeśli chcesz sprawdzić cały katalog, to samo zapytanie bez filtra, strona po stronie. Przy 20 tys. produktów trwa to kilkadziesiąt minut i wymaga sterowania tempem według nagłówków limitu API (licznik operacji i tempo jego zwalniania), nie stałej liczby zapytań na sekundę. Jak nie wpaść w 429 i nie zgubić stron wyników, opisuje poradnik Najczęstsze błędy Shoper API.
Dlaczego poprawka w panelu znika
Flagę można przestawić ręcznie w panelu, w zakładce wariantów produktu. Przy kilku produktach to wystarczy. Przy setkach już nie, a przy działającym imporcie nawet poprawka na setkach produktów jest tymczasowa.
Konektory BaseLinkera piszą do Shopera na zdarzeniach tworzenia i edycji produktu. W sklepie, w którym to badałem, w próbce 400 produktów każdy miał datę edycji z ostatnich trzech dni, więc import dotyka produktów praktycznie codziennie. Sama data edycji nie dowodzi jeszcze, że zmienia się akurat flaga domyślności. Dowodem było porównanie w czasie: 17 września 2026 roku, po pełnej naprawie, zawyżoną cenę miały 4 produkty. 15 października, bez żadnej korekty w międzyczasie, zrzut katalogu wskazywał już 177 produktów z flagą na droższym rekordzie, a na wierszu zbiorczym stała ona w około 11,7 tys. produktów, w większości z ceną równą najtańszemu rozmiarowi, więc niewidoczną dla klienta.
- poprawka w panelu albo skryptem jednorazowym: trzyma do czasu, aż import znów dotknie produktu, w tym sklepie średnio kilka produktów dziennie
- korekta cykliczna po stronie sklepu: usuwa skutki, nie przyczynę; między importem a korektą błąd jest widoczny
- zmiana w samym imporcie: jedyne rozwiązanie, które usuwa przyczynę
Co psuje poza ceną na liście
W sprawdzonych konfiguracjach ten sam rekord czytały też inne mechanizmy Shopera, więc jeden defekt dawał kilka objawów, zgłaszanych jako osobne problemy:
- panel: produkt widnieje jako „0 szt.”, bo wiersz zbiorczy ma stan 0, a stany siedzą w rozmiarach
- dane strukturalne karty:
OutOfStocki cena wiersza zbiorczego, a Merchant Center zgłasza „Niezgodna cena produktu” - filtr „Stan dostępności”: czyta rekord nadrzędny, więc pokazuje jako dostępny towar, którego nie ma, albo odwrotnie
- wbudowany plik produktowy Google: bierze stan i identyfikatory z rekordu głównego; przy stanach w rozmiarach wysyła prawie cały katalog jako niedostępny
Dwie pułapki przy automatycznej korekcie
Właściwa naprawa to przestawienie flagi default na dostępny rozmiar. Ceny zostają nietknięte, a cena na liście i karcie zmienia się od razu, bez wpływu na czas generowania strony. Dwa podejścia wyglądają na prostsze, ale zostawiają problem.
Nadpisanie ceny wiersza domyślnego ceną najtańszego rozmiaru usuwa objaw, ale zmienia cenę rekordu tylko po to, żeby poprawić prezentację, a rekord dalej nie jest rozmiarem do kupienia. Łatwo przy tym porównać pole price i pominąć comp_price, czyli cenę po promocji. Taki zapis trzeba robić z kopią, bo może wymagać cofnięcia.
Reguła, która przestawia flagę tylko wtedy, gdy wiersz domyślny jest droższy od najtańszego rozmiaru, pomija przypadek odwrotny: wiersz zbiorczy z ceną 1,99 zł jest tańszy od wszystkich rozmiarów, więc zostaje, a klient dalej widzi błędną cenę.
Który rozmiar ma zostać domyślny? Dostępny, ze stanem większym od zera. W naszych wdrożeniach najtańszy z dostępnych, bo to on odpowiada cenie „od”, którą klient widzi na liście. Reguła porównuje cenę regularną rekordów, a przy produktach bez żadnego dostępnego rozmiaru nic nie zmienia: flaga zostaje do czasu dostawy. Z wiersza zbiorczego flaga schodzi zawsze, niezależnie od ceny; wystarczy, że rekord nie jest realnym rozmiarem. Co przy równych cenach i jak zabezpieczyć przebieg przed nocą, w której import zmieni zachowanie, to już część reguły dopasowywana do sklepu.
Kiedy zrobisz to sam, a kiedy nie
Kilka do kilkudziesięciu produktów, brak integracji albo import, który nie dotyka wariantów. Panel wystarczy, a odczyt z API potwierdzi efekt.
Setki lub tysiące produktów i import, który pisze codziennie. Potrzebna jest reguła wyboru docelowego wariantu, korekta cykliczna z kopią stanu sprzed zmiany i odczytem kontrolnym, bezpiecznik na noc, w której import zmieni zachowanie, oraz zgłoszenie do źródła, bo tylko ono usuwa przyczynę.
Zgłoszenie do źródła warto zrobić od razu: poprosić właściciela danych w BaseLinkerze, żeby import ustawiał wariant domyślny na dostępny rozmiar. To jedno pole, a bez tego poprawka po stronie sklepu musi być powtarzana po każdym przebiegu. Kto powinien być właścicielem którego pola, opisuje poradnik Źródło prawdy: sklep, ERP, BaseLinker.
Szablon sklepu pomaga częściowo: na listingu może liczyć cenę od najtańszego dostępnego rozmiaru, a w danych strukturalnych karty dostępność z rozmiarów zamiast z wariantu domyślnego. Tak zrobiliśmy w jednym ze sklepów. Pliku produktowego Google i tego, co Shoper wysyła do Merchant Center, szablon nie zmieni. Tam decyduje wariant domyślny.