Prijs en referentieprijs automatisch bijwerken wanneer de leverancierskosten veranderen

September 3, 2026

Je leverancier verhoogt de kostprijs van een variant van 4,00 naar 5,20. In Shopify gebeurt er niets. De verkoopprijs blijft ongewijzigd en de marge die je had begroot, verdwijnt stilletjes, totdat iemand dat weken later tijdens een inventarisatie opmerkt.

Shopify Flow kan hier op zichzelf niet helpen, omdat Shopify geen ingebouwde trigger heeft voor een wijziging in de prijs per stuk. Er is ook geen actie waarmee de prijs op basis daarvan opnieuw wordt berekend.

Deze handleiding vult beide hiaten op. Wanneer de prijs per artikel voor een variant verandert, wordt er een Flow gestart en schrijft een korte functie de nieuwe prijs en de referentieprijs terug naar Shopify. Het instellen duurt ongeveer tien minuten, waarna het bij elke prijswijziging wordt uitgevoerd, voor altijd.

Wat je gaat bouwen

  1. Workflow Trigger Extensions detecteert de kostenwijziging en start een Flow, waarbij de variant, de bijbehorende SKU en zowel de oude als de nieuwe kosten worden meegenomen.
  2. Shopify Flow geeft die waarden als JSON door aan een functie.
  3. Workflow Functions voert een paar regels JavaScript uit die de nieuwe prijzen berekenen en deze via de Admin API terugsturen.

De prijsregel staat op één plek, in code waarover jij de controle hebt, dus als je deze later wilt aanpassen, hoef je slechts één regel te wijzigen in plaats van een workflow opnieuw op te bouwen.

Stap 1: Schakel de kostentrigger in

Ga in ‘Workflow Trigger Extensions’ naar de pagina ‘Triggers’, zoek de groep ‘Inventory’ en schakel ‘Product Variant Cost Changed’ in.

Deze trigger heeft toegang tot voorraadgegevens nodig, omdat de kosten per artikel aan het voorraadartikel zelf zijn gekoppeld en niet aan de variant. Als de toestemming nog niet is verleend, wordt er bij het inschakelen van de trigger in één dialoogvenster om gevraagd. Totdat de toestemming is verleend, blijft de trigger ingeschakeld, maar wordt er een melding ‘Toestemming vereist’ weergegeven en wordt de trigger niet geactiveerd.

Wat de trigger je biedt

Zodra het wordt geactiveerd, heeft je Flow toegang tot:

Veld Voorbeeld
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

Het product zelf wordt ook als productreferentie meegenomen, zodat je Flow-voorwaarden kunt instellen op basis van leverancier, type, tags of collectie voordat er iets aan de prijs wordt gewijzigd.

Stap 2: Maak de functie aan

Maak in **Workflow Functions** een nieuwe functie aan en plak de onderstaande code erin.

Schakel, voordat je opslaat, de optie ‘Shopify-gegevens vereist’ in. Hierdoor krijgt je code toegang tot ctx.shopify.graphql(...), zodat deze de winkelgegevens kan lezen en wijzigen. Open vervolgens de pagina ‘Toestemmingen’ op Workflow Functions en verleen toestemming voor het schrijven van producten; dit is het bereik dat deze functie nodig heeft om een prijs te wijzigen.

Zonder die schakelaar is het ctx.shopify-object niet beschikbaar en mislukt de functie bij de eerste Admin API-aanroep.

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

Een paar opmerkingen over de code:

  • Het accepteert zowel een naakte numerieke ID als een volledige GID. toGid zet beide om naar de juiste vorm, dus je hoeft je geen zorgen te maken over welke vorm Flow je aanreikt.
  • Het zoekt het bovenliggende product alleen op als dat nodig is. De functie productVariantsBulkUpdate vereist een product-ID; als je er geen doorgeeft, haalt de functie deze zelf op. Door productId als invoer door te geven, wordt die heen-en-terug-communicatie overgeslagen.
  • Het controleert op userErrors, niet alleen op transportfouten. Een GraphQL-aanroep kan een HTTP 200-status retourneren en toch het schrijven weigeren. Dit is de meest voorkomende reden waarom een prijsupdate lijkt te zijn gelukt, maar er niets verandert.
  • Het wissen van compare-at is opzettelijk. Als je compareAtPrice: null of een lege tekenreeks doorgeeft, wordt het verwijderd; dit wordt niet als een fout beschouwd.

Stap 3: Test de functie voordat je deze aansluit

Gebruik het tabblad ‘Test’ met een echte variant-ID uit je winkel. Neem er een uit een willekeurige product-URL in het beheergedeelte of uit de gebeurtenisgeschiedenis van de trigger.

Stel een prijs in die je herkent, zodat je kunt controleren of het bericht is aangekomen.

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

Stap 4: Sluit de Flow aan

Maak een workflow aan in Shopify Flow:

  1. Trigger: Kosten productvariant gewijzigd
  2. Actie: Voer de functie uit via Workflow Functions en selecteer de functie die je zojuist hebt aangemaakt

Plak dit in het JSON-invoerveld. Flow vervangt de Liquid-code bij elke uitvoering door daadwerkelijke waarden.

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

Dat is de volledige prijsregel: vijf keer de kostprijs als verkoopprijs, zeven keer de kostprijs als vergelijkingsprijs, zodat het product altijd een consistente korting laat zien.

Deze vermenigvuldigingsfactoren zijn slechts een voorbeeld, geen aanbeveling. Pas times: 5 aan naar wat jouw categorie daadwerkelijk ondersteunt. Liquid voert de berekeningen uit, dus een vaste opslag in plaats van een vermenigvuldigingsfactor is plus: 12, en een procentuele marge is times: 1.6.

Als je in meerdere valuta’s verkoopt, houd er dan rekening mee dat newCost.amount de valuta van de winkel weergeeft. Markets zorgt voor de omrekening op het moment van weergave, dus dat hoef je zelf niet te doen.

Controleer of het van begin tot eind werkt

  1. Open een willekeurige productvariant in het beheergedeelte van Shopify en wijzig de kosten per stuk
  2. Opslaan
  3. Binnen enkele seconden worden de prijs en de vergelijkingsprijs automatisch bijgewerkt

Als er niets gebeurt, werk dan in deze volgorde door de stappen heen, want elke stap sluit de vorige uit:

Controleer Waar
Is de trekker afgegaan? Evenementengeschiedenis in Workflow Trigger Extensions
Heeft de Flow gewerkt? Shopify Flow activiteitenlogboek
Heeft de functie gewerkt, en wat werd er in het logboek vastgelegd? Loopgeschiedenis in Workflow Functions

De meest voorkomende oorzaak van een run die als mislukt wordt gemeld, is een ontbrekende scope. De functie ctx.shopify is alleen beschikbaar als de optie ‘Needs Shopify data’ is ingeschakeld, en het schrijven lukt pas als toestemming voor het schrijven van producten is verleend.

Waar gaan we nu naartoe?

Hetzelfde patroon werkt voor alles wat door een kostenwijziging wordt beïnvloed. Door de functie-body aan te passen, kun je een product markeren voor controle wanneer de kosten met meer dan 10 procent stijgen, de oude en nieuwe marge op Slack plaatsen of de wijziging opslaan in een metaveld voor rapportagedoeleinden.

De trigger is het deel dat Shopify je niet geeft. Wat daarna volgt, is gewone code.

Related articleToestemmingen en toegang tot gegevensWat de toegang tot voorraadgegevens mogelijk maakt, en hoe je deze kunt verlenen of intrekken.Related articleVolledig overzicht van de trekkersAlle triggers in Workflow Trigger Extensions, met de velden die bij elke trigger horen.

Workflow Functions documentatie