BloggUppdatera priset och jämförelsepriset automatiskt när leverantörens kostnad ändras

Uppdatera priset och jämförelsepriset automatiskt när leverantörens kostnad ändras

September 3, 2026

Din leverantör höjer priset på en variant från 4,00 till 5,20. Ingenting i Shopify händer. Försäljningspriset förblir oförändrat, och den marginal du hade planerat försvinner obemärkt tills någon upptäcker det vid en inventering flera veckor senare.

Shopify Flow kan inte hjälpa till på egen hand här, eftersom Shopify inte har någon inbyggd utlösare för en ändring av kostnaden per artikel. Det finns heller ingen åtgärd som omberäknar priset utifrån den kostnaden.

Den här guiden fyller båda luckorna. När kostnaden per artikel ändras för någon variant startas ett Shopify Flow-flöde, och en kort funktion skriver tillbaka det nya priset och jämförelsepriset till Shopify. Det tar ungefär tio minuter att ställa in, och därefter körs det vid varje kostnadsändring, för alltid.

Vad du kommer att bygga

  1. Workflow Trigger Extensions upptäcker kostnadsförändringen och startar ett Shopify Flow, där varianten, dess SKU samt både den gamla och den nya kostnaden överförs.
  2. Shopify Flow överför dessa värden till en funktion i JSON-format.
  3. Workflow Functions-funktionerna kör några rader JavaScript-kod som beräknar de nya priserna och skickar tillbaka dem via Admin API.

Prissättningsregeln finns på ett enda ställe, i kod som du själv kontrollerar, så om du vill ändra den senare behöver du bara redigera en rad istället för att bygga om hela arbetsflödet.

Steg 1: Aktivera kostnadsutlösaren

I ”Workflow Trigger Extensions” öppnar du sidan ”Triggers”, letar upp gruppen ”Inventory” och aktiverar ”Product Variant Cost Changed”.

Denna trigger kräver åtkomst till lagerdata, eftersom kostnaden per artikel är kopplad till lagerartikeln och inte till själva varianten. Om behörigheten ännu inte har beviljats kommer en dialogruta att visas när du aktiverar triggern, där du ombeds att bevilja behörigheten. Tills behörigheten har beviljats förblir triggern aktiverad, men visar en flagga med texten ”Behörighet krävs” och utlöses inte.

Vad utlösaren ger dig

När den aktiveras får din Shopify Flow-process tillgång till:

Fält Exempel
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

Själva produkten ingår också som produktreferens, så att du kan lägga till villkor i Shopify Flow baserade på leverantör, typ, taggar eller kollektion innan priset påverkas.

Steg 2: Skapa funktionen

Skapa en ny funktion i ”Workflow Functions” och klistra in koden nedan.

Innan du sparar ska du aktivera alternativet ”Behöver Shopify-data”. Det är detta som gör att ctx.shopify.graphql(...) blir tillgängligt i din kod, så att den kan läsa och ändra butiksdata. Öppna sedan sidan ”Behörigheter” i Workflow Functions och bevilja behörigheten ”skriva produkter”, vilket är den behörighetsnivå som funktionen behöver för att kunna ändra ett pris.

Utan den inställningen är objektet ctx.shopify inte tillgängligt och funktionen misslyckas vid det första Admin API-anropet.

update-variant-price.jsjavascript
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 };
  }
}

Några saker som är värda att notera om koden:

  • Den accepterar både ett rent numeriskt ID och ett fullständigt GID. toGid omvandlar båda till standardformat, så du behöver inte oroa dig för vilken form Shopify Flow skickar till dig.
  • Den hämtar endast den överordnade produkten när det är nödvändigt. productVariantsBulkUpdate kräver ett produkt-ID; om du inte anger något hämtar funktionen det själv. Genom att ange productId i indata undviker du den extra hämtningen.
  • Den kontrollerar userErrors, inte bara transportfel. Ett GraphQL-anrop kan returnera HTTP 200 och ändå avslå skrivningen. Detta är den vanligaste orsaken till att en prisuppdatering verkar lyckas men att ingenting förändras.
  • Rensningen av compareAt är avsiktlig. Om man skickar in `compareAtPrice: null`` eller en tom sträng tas den bort, istället för att betraktas som ett fel.

Steg 3: Testa funktionen innan du ansluter den

Använd fliken ”Test” med ett riktigt variant-ID från din butik. Hämta ett från valfri produkt-URL i administratörsgränssnittet eller från utlösarens händelsehistorik.

Ange ett pris som du känner igen så att du kan bekräfta att meddelandet har kommit fram.

test-input.jsonjson
{
  "variantId": "gid://shopify/ProductVariant/54589751394579",
  "price": "24.99",
  "compareAtPrice": "39.99"
}

Steg 4: Koppla ihop Shopify Flow

Skapa ett arbetsflöde i Shopify Flow:

  1. Utlösare: Kostnaden för en produktvariant har ändrats
  2. Åtgärd: Kör funktionen från ”Workflow Functions” och välj den funktion du just har skapat

Klistra in detta i JSON-inmatningsfältet. Shopify Flow ersätter Liquid-koden med faktiska värden varje gång den körs.

flow-input.jsonjson
{
  "variantId": "{{variantCostChange.variantId}}",
  "price": "{{variantCostChange.newCost.amount | times: 5}}",
  "compareAtPrice": "{{variantCostChange.newCost.amount | times: 7}}"
}

Det är hela prissättningsregeln: fem gånger självkostnaden som försäljningspris, sju gånger självkostnaden som jämförelsepris, så att produkten alltid visas med en enhetlig rabatt.

Dessa multiplikatorer är ett exempel, inte en rekommendation. Ändra times: 5 till det värde som din kategori faktiskt stöder. Liquid sköter beräkningarna, så en fast påslagning istället för en multiplikator skrivs som plus: 12, och en procentuell marginal som times: 1.6.

Om du säljer i flera valutor bör du tänka på att newCost.amount anges i butikens valuta. Markets sköter omräkningen när priset visas, så du behöver inte göra det själv.

Kontrollera att det fungerar från början till slut

  1. Öppna valfri produktvariant i Shopify-administrationspanelen och ändra kostnaden per artikel
  2. Spara
  3. Inom några sekunder uppdateras priset och jämförelsepriset automatiskt

Om ingenting händer, gå igenom stegen i den här ordningen, eftersom varje steg utesluter det föregående:

Kontrollera Var
Avfyrades avtryckaren? Händelsehistorik i Workflow Trigger Extensions
Har Shopify Flow-processen körts? Shopify Flow-aktivitetslogg
Kördes funktionen, och vad visade loggen? Körhistorik i Workflow Functions

Den vanligaste orsaken till att en körning rapporterar ett fel är att ett scope saknas. ctx.shopify är endast tillgängligt när ”Behöver Shopify-data” är aktiverat, och skrivningen lyckas först när behörigheten att skriva till produkter har beviljats.

Vart ska man ta vägen härnäst?

Samma mönster fungerar för allt som påverkas av en kostnadsförändring. Byt ut funktionskroppen så kan du markera en produkt för granskning när kostnaden stiger med mer än 10 procent, publicera den gamla och den nya marginalen på Slack eller skriva in förändringen i ett metafält för rapportering.

Triggern är den del som Shopify inte tillhandahåller. Det som händer därefter är vanlig kod.

Related articleBehörigheter och åtkomst till dataVad åtkomst till lagerdata ger för möjligheter, och hur man beviljar eller återkallar den.Related articleFullständig referens för avtryckareAlla utlösare i ”Workflow Trigger Extensions”, tillsammans med de fält som var och en innehåller.

Dokumentation om Workflow Functions