Automatyczna aktualizacja ceny i ceny porównawczej w przypadku zmiany kosztu u dostawcy
Państwa dostawca podnosi cenę wariantu z 4,00 do 5,20. W systemie Shopify nie następuje żadna reakcja. Cena detaliczna pozostaje na dotychczasowym poziomie, a zaplanowana marża po cichu znika, dopóki ktoś tego nie zauważy podczas inwentaryzacji kilka tygodni później.
Shopify Flow nie może w tym przypadku samodzielnie pomóc, ponieważ Shopify nie posiada wbudowanego wyzwalacza dla zmiany kosztu jednostkowego. Nie ma również akcji, która na tej podstawie ponownie obliczałaby cenę.
Niniejszy przewodnik wypełnia obie te luki. Gdy koszt jednostkowy jakiegokolwiek wariantu ulegnie zmianie, uruchamia się proces w Shopify Flow, a krótka funkcja zapisuje nową cenę oraz cenę porównawczą z powrotem w Shopify. Konfiguracja zajmuje około dziesięciu minut, a następnie proces ten uruchamia się przy każdej zmianie kosztu - i to bezterminowo.
Co Państwo stworzą
- Rozszerzenie Workflow Trigger Extensions wykrywa zmianę ceny i uruchamia proces Shopify Flow, przekazując w nim wariant produktu, jego numer SKU oraz zarówno starą, jak i nową cenę.
- Shopify Flow przekazuje te wartości do funkcji w formacie JSON.
- Funkcje Workflow Functions uruchamiają kilka wierszy kodu JavaScript, które obliczają nowe ceny i zapisują je z powrotem za pośrednictwem interfejsu API administratora.
Reguła ustalania cen znajduje się w jednym miejscu, w kodzie, nad którym mają Państwo kontrolę, więc jej późniejsza zmiana wymaga jedynie edycji jednego wiersza, a nie przebudowy całego przepływu pracy.
Krok 1: Włącz wyzwalacz kosztowy
W sekcji Workflow Trigger Extensions proszę otworzyć stronę Triggers, znaleźć grupę Inventory i włączyć opcję Product Variant Cost Changed.
Ten wyzwalacz wymaga uprawnień dostępu do danych magazynowych, ponieważ koszt jednostkowy jest przypisany do pozycji magazynowej, a nie do samego wariantu. Jeśli uprawnienie nie zostało jeszcze przyznane, włączenie wyzwalacza spowoduje wyświetlenie prośby o jego przyznanie w jednym oknie dialogowym. Dopóki uprawnienie nie zostanie przyznane, wyzwalacz pozostaje włączony, ale wyświetla komunikat Wymagane uprawnienie i nie zostanie uruchomiony.
Co zapewnia ten element wyzwalający
Po uruchomieniu Państwa Shopify Flow ma dostęp do:
| Dziedzina | Przykład |
|---|---|
variantCostChange.variantId |
gid://shopify/ProductVariant/54589751394579 |
variantCostChange.variantTitle |
Medium / Black |
variantCostChange.sku |
TSHIRT-M-BLK |
variantCostChange.oldCost.amount |
4.00 |
variantCostChange.newCost.amount |
5.20 |
variantCostChange.newCost.currencyCode |
EUR |
Sam produkt jest również uwzględniony jako odniesienie do produktu, dzięki czemu mogą Państwo dodać warunki w Shopify Flow’ oparte na dostawcy, typie, tagach lub kolekcji, zanim cena zostanie w jakikolwiek sposób zmieniona.
Krok 2: Utwórz funkcję
W sekcji Workflow Functions proszę utworzyć nową funkcję i wkleić poniższy kod.
Przed zapisaniem należy włączyć opcję „Wymagane dane Shopify”. Dzięki temu w Państwa kodzie zostanie udostępniona zmienna ctx.shopify.graphql(...), umożliwiająca odczyt i modyfikację danych sklepu. Następnie proszę otworzyć stronę Uprawnienia w sekcji Workflow Functions i przyznać uprawnienie do zapisu produktów - jest to zakres uprawnień niezbędny tej funkcji do zmiany ceny.
Bez tego przełącznika obiekt ctx.shopify nie jest dostępny, a funkcja kończy się niepowodzeniem już przy pierwszym wywołaniu interfejsu API panelu administracyjnego.
export default async function (input, ctx) {
const toGid = (value, type) => {
if (value === undefined || value === null) return null;
const str = String(value).trim();
if (!str) return null;
if (str.startsWith('gid://')) return str;
if (/^\d+$/.test(str)) return `gid://shopify/${type}/${str}`;
return str;
};
try {
const variantId = toGid(input.variantId || input.variant_id || input.id, 'ProductVariant');
if (!variantId) throw new Error('Missing "variantId" in input.');
const hasPrice = input.price !== undefined && input.price !== null && String(input.price).trim() !== '';
const hasCompareAt = Object.prototype.hasOwnProperty.call(input, 'compareAtPrice') || Object.prototype.hasOwnProperty.call(input, 'compare_at_price');
const rawCompareAt = input.compareAtPrice !== undefined ? input.compareAtPrice : input.compare_at_price;
if (!hasPrice && !hasCompareAt) throw new Error('Nothing to update: provide "price" and/or "compareAtPrice".');
const asMoney = (value) => {
const num = Number(value);
if (!Number.isFinite(num) || num < 0) throw new Error(`Invalid money value: ${JSON.stringify(value)}`);
return num.toFixed(2);
};
let productId = toGid(input.productId || input.product_id, 'Product');
if (!productId) {
const readQuery = `
query VariantForPriceUpdate($id: ID!) {
productVariant(id: $id) { id product { id } }
}`;
const readRes = await ctx.shopify.graphql(readQuery, { id: variantId });
if (readRes.errors) throw new Error('Failed to read variant: ' + JSON.stringify(readRes.errors));
const variant = readRes.data && readRes.data.productVariant;
if (!variant) throw new Error(`Variant not found: ${variantId}`);
productId = variant.product && variant.product.id;
if (!productId) throw new Error('Could not resolve the parent product id for the variant.');
}
const variantInput = { id: variantId };
if (hasPrice) variantInput.price = asMoney(input.price);
if (hasCompareAt) {
variantInput.compareAtPrice = rawCompareAt === null || String(rawCompareAt).trim() === '' ? null : asMoney(rawCompareAt);
}
ctx.log('Updating variant', variantId, 'on product', productId, variantInput);
const mutation = `
mutation UpdateVariantPrice($productId: ID!, $variants: [ProductVariantsBulkInput!]!) {
productVariantsBulkUpdate(productId: $productId, variants: $variants) {
productVariants { id }
userErrors { field message }
}
}`;
const res = await ctx.shopify.graphql(mutation, { productId, variants: [variantInput] });
if (res.errors) throw new Error('GraphQL error updating variant: ' + JSON.stringify(res.errors));
const payload = res.data && res.data.productVariantsBulkUpdate;
if (!payload) throw new Error('Unexpected empty response from productVariantsBulkUpdate.');
if (payload.userErrors && payload.userErrors.length) {
throw new Error('Variant update failed: ' + payload.userErrors.map(e => `${(e.field || []).join('.')}: ${e.message}`).join('; '));
}
return { success: true };
} catch (err) {
ctx.log('Error updating variant price:', err.message);
return { success: false };
}
}Kilka kwestii wartych odnotowania w odniesieniu do kodu:
- Akceptuje sam identyfikator numeryczny lub pełny identyfikator GID. Funkcja
toGidnormalizuje obie formy, więc nie muszą Państwo martwić się o to, w jakiej postaci identyfikator zostanie przekazany przez Shopify Flow. - Funkcja wyszukuje produkt nadrzędny tylko wtedy, gdy jest to konieczne. Metoda
productVariantsBulkUpdatewymaga podania identyfikatora produktu; jeśli nie zostanie on przekazany, funkcja pobiera go samodzielnie. Przekazanie parametruproductIdw danych wejściowych pozwala uniknąć tej wymiany danych. - Sprawdza on
userErrors, a nie tylko błędy transportowe. Wywołanie GraphQL może zwrócić kod HTTP 200, a mimo to odmówić zapisu. Jest to najczęstsza przyczyna sytuacji, w której aktualizacja ceny wydaje się zakończona sukcesem, ale w rzeczywistości nic się nie zmienia. - Usunięcie parametru
compareAtjest zamierzone. Przekazanie wartości `compareAtPrice: null`` lub pustego ciągu znaków powoduje jego usunięcie, a nie jest traktowane jako błąd.
Krok 3: Proszę przetestować działanie funkcji przed podłączeniem przewodów
Proszę skorzystać z zakładki Test, podając rzeczywisty identyfikator wariantu ze swojego sklepu. Proszę pobrać go z dowolnego adresu URL produktu w panelu administracyjnym lub z historii zdarzeń wyzwalacza.
Proszę ustawić cenę, którą Pan/Pani rozpozna, aby móc potwierdzić, że zlecenie dotarło do adresata.
{
"variantId": "gid://shopify/ProductVariant/54589751394579",
"price": "24.99",
"compareAtPrice": "39.99"
}Krok 4: Skonfiguruj połączenie w Shopify Flow
Proszę utworzyć przepływ pracy w usłudze Shopify Flow:
- Wyzwalacz: Zmiana ceny wariantu produktu
- Czynność: Proszę uruchomić funkcję z sekcji Workflow Functions i wybrać właśnie utworzoną funkcję
W polu wprowadzania danych JSON proszę wkleić poniższy tekst. Shopify Flow zastępuje kod Liquid rzeczywistymi wartościami przy każdym uruchomieniu.
{
"variantId": "{{variantCostChange.variantId}}",
"price": "{{variantCostChange.newCost.amount | times: 5}}",
"compareAtPrice": "{{variantCostChange.newCost.amount | times: 7}}"
}Oto cała zasada ustalania cen: cena sprzedaży wynosi pięciokrotność kosztu, a cena porównawcza - siedmiokrotność kosztu, dzięki czemu produkt zawsze jest wyświetlany z niezmiennym rabatem.
Podane mnożniki mają charakter przykładowy, a nie zalecenia. Proszę zmienić wartość times: 5 na wartość faktycznie obsługiwanej przez daną kategorię. Liquid zajmuje się obliczeniami, więc stała wartość w znacznikach zamiast mnożnika to plus: 12, a marża procentowa to times: 1.6.
Jeśli prowadzą Państwo sprzedaż w kilku walutach, proszę pamiętać, że wartość newCost.amount jest podana w walucie sklepu. System Markets dokonuje przeliczenia w momencie wyświetlania, więc nie muszą Państwo tego robić samodzielnie.
Proszę sprawdzić, czy wszystko działa od początku do końca
- Proszę otworzyć dowolny wariant produktu w panelu administracyjnym Shopify i zmienić koszt jednostkowy
- Zapisz
- W ciągu kilku sekund cena oraz cena porównawcza aktualizują się automatycznie
Jeśli nic się nie dzieje, proszę postępować zgodnie z poniższą kolejnością, ponieważ każdy kolejny krok wyklucza poprzedni:
| Sprawdź | Gdzie |
|---|---|
| Czy spust zadziałał? | Historia zdarzeń w rozszerzeniach wyzwalaczy przepływu pracy (Workflow Trigger Extensions) |
| Czy proces Shopify Flow został uruchomiony? | Dziennik aktywności Shopify Flow |
| Czy funkcja została uruchomiona i jakie informacje zostały zapisane w dzienniku? | Historia uruchomień w funkcjach przepływu pracy (Workflow Functions) |
Najczęstszą przyczyną niepowodzenia zadania jest brak zakresu. ctx.shopify jest dostępne tylko wtedy, gdy włączona jest opcja „Wymagane dane Shopify”, a zapis zakończy się powodzeniem dopiero po przyznaniu uprawnień do zapisywania produktów.
Gdzie udać się dalej
Ten sam schemat sprawdza się w przypadku każdego elementu, na który powinna wpłynąć zmiana kosztów. Wystarczy zamienić treść funkcji, aby oznaczyć produkt do weryfikacji w przypadku wzrostu kosztów o ponad 10 procent, opublikować stare i nowe marże na Slacku lub zapisać tę zmianę w metapolu na potrzeby raportowania.
Wyzwalacz to element, którego firma Shopify nie udostępnia. Dalszy przebieg to zwykły kod.
Dokumentacja funkcji Workflow Functions
- Uprawnienia i dostęp do danych sklepu - włączenie opcji „Wymagane dane Shopify” oraz przyznanie zakresów uprawnień
- Przekazywanie zmiennych do funkcji - jak działają dane wejściowe w formacie JSON i Liquid
- Konfiguracja akcji „Uruchom funkcję” w Shopify Flow
- Historia uruchomień i rozwiązywanie problemów
