BloggOppdater pris og sammenligningspris automatisk når leverandørprisen endres

Oppdater pris og sammenligningspris automatisk når leverandørprisen endres

September 3, 2026

Leverandøren din øker prisen på en variant fra 4,00 til 5,20. Ingenting i Shopify reagerer på dette. Salgsprisen forblir uendret, og marginen du hadde planlagt, forsvinner i det stille helt til noen oppdager det under en lageropptelling flere uker senere.

Shopify Flow kan ikke hjelpe her på egen hånd, fordi Shopify ikke har noen innebygd utløser for endringer i kostnaden per vare. Det finnes heller ingen handling som beregner prisen på nytt ut fra denne kostnaden.

Denne veiledningen løser begge problemene. Når enhetsprisen endres på en hvilken som helst variant, startes en Shopify Flow, og en kort funksjon sender den nye prisen og sammenligningsprisen tilbake til Shopify. Det tar omtrent ti minutter å sette opp, og deretter kjører den ved hver prisendring - for alltid.

Hva du skal bygge

  1. Workflow Trigger Extensions registrerer endringen i kostnaden og setter i gang en Shopify Flow, som inneholder varianten, dens SKU samt både den gamle og den nye kostnaden.
  2. Shopify Flow sender disse verdiene til en funksjon i JSON-format.
  3. Workflow Functions kjører noen få linjer med JavaScript som beregner de nye prisene og sender dem tilbake via Admin API.

Prisreglene finnes på ett sted, i koden du selv har kontroll over, så hvis du vil endre dem senere, trenger du bare å redigere én linje i stedet for å bygge om hele arbeidsflyten.

Trinn 1: Slå på kostnadsutløseren

I Workflow Trigger Extensions åpner du siden Triggers, finner gruppen Inventory og aktiverer Product Variant Cost Changed.

Denne utløseren krever tilgang til lagerdata, fordi kostnaden per vare er knyttet til lagerartikkelen og ikke til selve varianten. Hvis tillatelsen ikke er gitt ennå, vil du bli bedt om å gi den i en enkelt dialogboks når du slår på utløseren. Inntil tillatelsen er gitt, forblir utløseren aktiv, men viser en melding om at tillatelse kreves og vil ikke utløses.

Hva utløseren gir deg

Når den utløses, har Shopify Flow-prosessen din tilgang til:

Felt Eksempel
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

Selve produktet er også inkludert som en produktreferanse, slik at du kan legge til Shopify Flow-betingelser basert på leverandør, type, tagger eller kolleksjon før prisen endres.

Trinn 2: Opprett funksjonen

I Workflow Functions oppretter du en ny funksjon og limer inn koden nedenfor.

Før du lagrer, må du slå på «Trenger Shopify-data». Det er dette som gjør ctx.shopify.graphql(...) tilgjengelig i koden din, slik at den kan lese og endre butikkdata. Åpne deretter siden Tillatelser i Workflow Functions, og gi tillatelse til å skrive til produkter - dette er omfanget denne funksjonen trenger for å endre en pris.

Uten denne bryteren er ikke ctx.shopify-objektet tilgjengelig, og funksjonen mislykkes ved det første Admin API-kallet.

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 };
  }
}

Noen ting det er verdt å merke seg om koden:

  • Den godtar enten en ren numerisk ID eller en fullstendig GID. toGid omformer begge deler, så du trenger ikke å bekymre deg for hvilken form Shopify Flow gir deg.
  • Den henter opp overordnet produkt bare når det er nødvendig. productVariantsBulkUpdate krever en produkt-ID; hvis du ikke oppgir en, henter funksjonen den selv. Ved å oppgi productId i inndataene unngår du denne turen frem og tilbake.
  • Den sjekker userErrors, ikke bare transportfeil. Et GraphQL-anrop kan returnere HTTP 200 og likevel avvise skrivingen. Dette er den vanligste årsaken til at en prisoppdatering ser ut til å lykkes, men at ingenting endres.
  • Det er meningen at compareAt skal fjernes. Hvis man sender inn `compareAtPrice: null`` eller en tom streng, blir den fjernet, i stedet for at det blir tolket som en feil.

Trinn 3: Test funksjonen før du kobler den til

Bruk fanen Test med en ekte variant-ID fra nettbutikken din. Hent en fra en hvilken som helst produkt-URL i administrasjonspanelet, eller fra utløserens hendelseshistorikk.

Angi en pris du kjenner igjen, slik at du kan bekrefte at meldingen er mottatt.

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

Trinn 4: Konfigurer Shopify Flow

Opprett en arbeidsflyt i Shopify Flow:

  1. Utløser: Endring i kostnaden for en produktvariant
  2. Handling: Kjør funksjon fra Workflow Functions, og velg funksjonen du nettopp har opprettet

Lim inn dette i JSON-inndatafeltet. Shopify Flow erstatter Liquid-koden med reelle verdier hver gang den kjøres.

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

Det er hele prisregelen: fem ganger kostprisen som salgspris, syv ganger kostprisen som sammenligningspris, slik at produktet alltid vises med en jevn rabatt.

Disse multiplikatorene er et eksempel, ikke en anbefaling. Endre times: 5 til det tallet som din kategori faktisk støtter. Liquid tar seg av regnestykket, så et fast påslag i stedet for en multiplikator er plus: 12, og en prosentvis margin er times: 1.6.

Hvis du selger i flere valutaer, må du være oppmerksom på at newCost.amount er angitt i butikkens valuta. Markets tar seg av valutaomregningen når beløpet vises, så du trenger ikke å gjøre det selv.

Bekreft at det fungerer fra start til slutt

  1. Åpne en hvilken som helst produktvariant i Shopify-administrasjonspanelet og endre Kostnad per enhet
  2. Lagre
  3. I løpet av få sekunder oppdateres prisen og sammenligningsprisen automatisk

Hvis ingenting skjer, må du gå gjennom trinnene i denne rekkefølgen, siden hvert trinn utelukker det forrige:

Sjekk Hvor
Gikk avtrekkeren av? Hendelseshistorikk i utvidelser for arbeidsflyt-utløsere (Workflow Trigger Extensions)
Ble Shopify Flow-prosessen kjørt? Shopify Flow-aktivitetslogg
Ble funksjonen kjørt, og hva ble logget? Kjør historikk i Workflow Functions

Den vanligste årsaken til at en kjøring melder feil, er at et omfang mangler. ctx.shopify er kun tilgjengelig når «Trenger Shopify-data» er aktivert, og skrivingen lykkes først når tillatelse til å skrive produkter er gitt.

Hvor skal vi dra videre?

Det samme mønsteret gjelder for alt som påvirkes av en kostnadsendring. Ved å bytte om funksjonskroppen kan du merke et produkt for gjennomgang når kostnaden stiger med mer enn 10 prosent, legge ut den gamle og den nye marginen på Slack eller skrive endringen til et metafelt for rapportering.

Utløseren er den delen som Shopify ikke gir deg. Det som skjer videre, er vanlig kode.

Related articleTillatelser og datatilgangHva tilgang til lagerdata gir mulighet for, og hvordan man gir eller trekker tilbake denne tilgangen.Related articleFullstendig oversikt over avtrekkereAlle utløsere i Workflow Trigger Extensions, med feltene som hver enkelt inneholder.

Dokumentasjon for Workflow Functions