Przejdź do głównej zawartości

Najniższa cena przed obniżką (Omnibus)

Przepisy o informowaniu o cenach w UE (w Polsce: ustawa o informowaniu o cenach, tzw. dyrektywa Omnibus) każą przy każdej ogłaszanej obniżce pokazać najniższą cenę, po jakiej produkt był sprzedawany w ciągu 30 dni przed wprowadzeniem obniżki. Platforma liczy tę cenę z historii cen i oddaje ją w polu priceReduction — Ty tylko ją renderujesz.

Skąd biorą się dane

  • ProductVariant.priceReduction — na stronie produktu i w koszyku (variant).
  • Product.priceReduction — na kartach produktów (listing, wyszukiwarka): referencja dla wariantu, którego cena tworzy priceRange.minVariantPrice, czyli tego, którego obniżkę karta ogłasza przez compareAtPriceRange.

Oba pola są null, gdy: wariant nie jest przeceniony (brak compareAtPrice powyżej ceny), merchant nie włączył w panelu aplikacji „Omnibus", albo platforma nie potrafi udowodnić ceny referencyjnej (np. produkt był oferowany przed rozpoczęciem śledzenia historii, a obniżka zaczęła się zanim minęło 30 dni obserwacji). W każdym z tych przypadków nie pokazuj zdania o najniższej cenie — a przy null mimo przekreślonej ceny rozważ, czy w ogóle eksponować rabat.

Fragment PriceReduction jest już rozwinięty w ProductCard i ProductFull, więc useProduct i listingi z SDK niosą pole automatycznie.

fragment PriceReduction on PriceReductionInfo {
lowestPriorPrice { amount currencyCode }
reductionStartedAt
referenceWindowStartedAt
referenceWindowTruncated
percentageOffLowestPrior
label
}
PoleCo znaczy
lowestPriorPriceNajniższa cena z okna referencyjnego, w walucie sklepu. Pokaż ją co najmniej tak wyraźnie jak cenę bieżącą.
lowestPriorPriceWithConversionOpt-in: ta sama kwota przeliczona na walutę kupującego tym samym kursem co priceWithConversion.
reductionStartedAtKiedy wprowadzono bieżącą obniżkę (każde pogłębienie promocji to nowa obniżka).
referenceWindowStartedAt / referenceWindowTruncatedPoczątek okna; true, gdy okno jest krótsze niż 30 dni (produkt oferowany krócej) — referencja liczy się wtedy od początku oferowania.
percentageOffLowestPriorRabat liczony względem najniższej ceny, nie względem przekreślonej. Zero lub mniej = nie reklamuj rabatu.
labelGotowe zdanie prawne w języku sklepu z wpisaną kwotą (merchant może nadać własne brzmienie w panelu). Renderuj dosłownie.

Strona produktu

'use client';

import { useProduct } from '@doswiftly/storefront-sdk/react';

export function VariantPrice({ handle }: { handle: string }) {
const { data: product } = useProduct({ handle });
const variant = product?.variants.nodes[0];
if (!variant?.price) return null;

const reduction = variant.priceReduction;
const onSale = Boolean(variant.compareAtPrice && reduction);

return (
<div>
{onSale ? (
<>
<s>{variant.compareAtPrice!.amount} {variant.compareAtPrice!.currencyCode}</s>{' '}
<strong>{variant.price.amount} {variant.price.currencyCode}</strong>
<p>{reduction!.label}</p>
{reduction!.percentageOffLowestPrior > 0 ? (
<span>-{reduction!.percentageOffLowestPrior}%</span>
) : null}
</>
) : (
<strong>{variant.price.amount} {variant.price.currencyCode}</strong>
)}
</div>
);
}

Dwie zasady, które ten przykład egzekwuje:

  1. Przekreślona cena tylko razem ze zdaniem prawnym. onSale wymaga obu pól — samo compareAtPrice bez priceReduction nie ogłasza obniżki.
  2. Procent z backendu, nie z własnego dzielenia. percentageOffLowestPrior liczy się względem najniższej ceny z okna; rabat „względem przekreślonej" bywa wyższy i to on jest przedmiotem kar.

Karta produktu na listingu

const reduction = product.priceReduction;
{product.compareAtPriceRange && reduction ? (
<small>{reduction.label}</small>
) : null}

Listing ogłasza obniżkę przez compareAtPriceRange, więc obowiązek informacyjny dotyczy także kart — nie tylko strony produktu.

Koszyk

Linie koszyka niosą variant, ale wspólny fragment ProductVariant nie zawiera priceReduction (koszyk nie ogłasza obniżki). Jeśli pokazujesz w koszyku przekreśloną cenę i chcesz przy niej zdania prawnego, dodaj we własnej operacji variant { priceReduction { ...PriceReduction } }.

Wielowalutowość

Kwota w label i w lowestPriorPrice jest w walucie sklepu. Sklep z przełącznikiem walut renderuje lowestPriorPriceWithConversion obok priceWithConversion — obie przeliczone tym samym kursem w tym samym momencie.

Powiązane