Opdater pris og sammenligningspris automatisk, når leverandørprisen ændrer sig

September 3, 2026

Din leverandør hæver prisen på en variant fra 4,00 til 5,20. Intet i Shopify reagerer på det. Salgsprisen forbliver uændret, og den margin, du havde planlagt, forsvinder stille og roligt, indtil nogen opdager det under en lageropgørelse nogle uger senere.

Shopify Flow Det kan ikke løse problemet på egen hånd her, fordi Shopify ikke har nogen indbygget udløser for en ændring af prisen pr. vare. Der er heller ingen handling, der genberegner prisen ud fra denne værdi.

Denne vejledning løser begge problemer. Når prisen pr. vare ændres for en hvilken som helst variant, startes der et Flow, og en kort funktion skriver den nye pris og sammenligningsprisen tilbage til Shopify. Det tager cirka ti minutter at sætte op, og derefter kører det ved hver prisændring - for altid.

Hvad du skal bygge

  1. Workflow Trigger Extensions registrerer ændringen i prisen og starter et Flow, der indeholder varianten, dens SKU samt både den gamle og den nye pris.
  2. Shopify Flow overfører disse værdier til en funktion som JSON.
  3. Workflow Functions kører et par linjer JavaScript, der beregner de nye priser og sender dem tilbage via Admin API’et.

Prisreglen findes ét sted, nemlig i den kode, du selv styrer, så hvis du senere vil ændre den, behøver du blot at redigere én linje i stedet for at genopbygge en hel arbejdsgang.

Trin 1: Aktivér omkostningstriggeren

I »Workflow Trigger Extensions« skal du åbne siden »Triggers«, finde gruppen »Inventory« og aktivere »Product Variant Cost Changed«.

Denne trigger kræver adgang til lagerdata, da enhedsprisen er knyttet til lagerartiklen og ikke til selve varianten. Hvis tilladelsen endnu ikke er tildelt, vil der blive bedt om den i en enkelt dialogboks, når triggeren aktiveres. Indtil tilladelsen er tildelt, forbliver triggeren aktiveret, men der vises en markering om, at der kræves tilladelse, og den udløses ikke.

Hvad udløseren giver dig

Når den udløses, har din Flow adgang 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å medtaget som produktreference, så du kan tilføje Flow-betingelser baseret på leverandør, type, tags eller samling, inden prisen overhovedet berøres.

Trin 2: Opret funktionen

I filen »Workflow Functions« skal du oprette en ny funktion og indsætte nedenstående kode.

Inden du gemmer, skal du aktivere »Behov for Shopify-data«. Det er det, der giver din kode adgang til ctx.shopify.graphql(...), så den kan læse og ændre butiksdata. Åbn derefter siden »Tilladelser« på Workflow Functions og tildel skriveadgang til produkter, hvilket er det omfang, denne funktion har brug for for at kunne ændre en pris.

Uden denne indstilling er objektet ctx.shopify ikke tilgængeligt, og funktionen mislykkes ved det første Admin API-kald.

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

Et par ting, der er værd at bemærke ved koden:

  • Den accepterer enten et simpelt numerisk id eller et fuldt GID. toGid normaliserer begge dele, så du behøver ikke bekymre dig om, hvilken form Flow leverer.
  • Den søger kun efter overordnede produkter, når det er nødvendigt. Funktionen »productVariantsBulkUpdate« kræver et produkt-id; hvis du ikke angiver et, henter funktionen det selv. Ved at angive »productId« i indgangen undgår man denne frem- og tilbagegang.
  • Den kontrollerer userErrors, ikke blot transportfejl. Et GraphQL-kald kan returnere en HTTP 200-status og alligevel afvise skrivningen. Dette er den mest almindelige årsag til, at en prisopdatering ser ud til at lykkes, men der ikke sker nogen ændringer.
  • Sletning af »compare-at« er tilsigtet. Hvis man overfører »compareAtPrice: null« eller en tom streng, fjernes den, i stedet for at det betragtes som en fejl.

Trin 3: Test funktionen, før du tilslutter den

Brug fanen »Test« med et rigtigt variant-id fra din butik. Vælg et fra en vilkårlig produkt-URL i adminpanelet eller fra triggerens hændelseshistorik.

Indstil en pris, du genkender, så du kan bekræfte, at beskeden er modtaget.

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

Trin 4: Tilslut Flow

Opret en arbejdsgang i Shopify Flow:

  1. Udløser: Ændring i pris på produktvariant
  2. Handling: Kør funktion fra »Workflow Functions«, og vælg den funktion, du netop har oprettet

Indsæt dette i JSON-indtastningsfeltet. Flow erstatter Liquid-koden med reelle værdier, hver gang det kører.

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

Det er hele prisreglen: fem gange kostprisen som salgspris, syv gange kostprisen som sammenligningspris, så produktet altid vises med en ensartet rabat.

Disse multiplikatorer er blot et eksempel, ikke en anbefaling. Erstat times: 5 med det tal, som din kategori rent faktisk understøtter. Liquid tager sig af beregningerne, så et fast påslag i stedet for en multiplikator er plus: 12, og en procentuel margin er times: 1.6.

Hvis du sælger i flere valutaer, skal du være opmærksom på, at newCost.amount er angivet i butikkens valuta. Markets tager sig af valutaomregningen, når siden vises, så du behøver ikke selv at gøre det.

Bekræft, at det fungerer fra start til slut

  1. Åbn en vilkårlig produktvariant i Shopify-administrationspanelet, og rediger prisen pr. enhed
  2. Gem
  3. Inden for få sekunder opdateres prisen og sammenligningsprisen automatisk

Hvis der ikke sker noget, skal du følge vejledningen i denne rækkefølge, da hvert trin udelukker det foregående:

Tjek Hvor
Gik aftrækkeren af? Begivenhedshistorik i »Workflow Trigger Extensions«
Kørte Flow? Shopify Flow aktivitetslog
Kørte funktionen, og hvad blev der logget? Kørselshistorik i Workflow Functions

Den mest almindelige årsag til, at et kørsel rapporterer en fejl, er et manglende scope. »ctx.shopify« er kun tilgængeligt, når indstillingen »Needs Shopify« er aktiveret, og skrivningen lykkes først, når der er tildelt skriveadgang.

Hvor skal vi hen nu?

Det samme mønster gælder for alt, hvad en prisændring bør påvirke. Byt om på funktionskroppen, og du kan markere et produkt til gennemgang, når prisen stiger med mere end 10 procent, sende den gamle og den nye fortjenstmargen til Slack eller skrive ændringen til et metafelt til brug for rapportering.

Triggeren er den del, som Shopify ikke giver dig. Det, der sker derefter, er almindelig kode.

Related articleTilladelser og dataadgangHvilke muligheder giver adgang til lagerdata, og hvordan man tildeler eller tilbagekalder denne adgang.Related articleKomplet oversigt over udløsereAlle triggere i Workflow Trigger Extensions med de felter, de hver især indeholder.

Workflow Functions dokumentation