Actualizar automáticamente el precio y el precio de referencia cuando cambie el coste del proveedor

September 3, 2026

Tu proveedor sube el coste de una variante de 4,00 a 5,20. En Shopify no ocurre nada. El precio de venta al público se mantiene igual, y el margen que habías previsto desaparece silenciosamente hasta que alguien se da cuenta durante un inventario semanas más tarde.

Shopify Flow Esto no se puede solucionar por sí solo, ya que Shopify no dispone de un activador integrado para modificar el coste por unidad. Tampoco cuenta con ninguna acción que permita recalcular el precio a partir de ese coste.

Esta guía resuelve ambos problemas. Cuando cambia el coste por unidad de cualquier variante, se inicia un «Flow» y una función breve vuelve a introducir el nuevo precio y el precio de referencia en Shopify. La configuración lleva unos diez minutos y, a partir de entonces, se ejecuta cada vez que cambia el coste, de forma permanente.

Lo que vas a crear

  1. Workflow Trigger Extensions detecta el cambio en el coste e inicia un flujo, incluyendo la variante, su SKU y tanto el coste anterior como el nuevo.
  2. Shopify Flow pasa esos valores a una función en formato JSON.
  3. Workflow Functions ejecuta unas cuantas líneas de JavaScript que calculan los nuevos precios y los envían de vuelta a través de la API de administración.

La regla de fijación de precios se encuentra en un único lugar, en código que tú controlas, por lo que modificarla más adelante solo implica editar una línea, en lugar de tener que reconstruir todo un flujo de trabajo.

Paso 1: Activar el umbral de coste

En Workflow Trigger Extensions, abre la página «Triggers», busca el grupo «Inventario» y activa la opción «Product Variant Cost Changed».

Este disparador necesita acceso a los datos de inventario, ya que el coste por artículo se almacena en la ficha del artículo de inventario y no en la propia variante. Si aún no se ha concedido el permiso, al activar el disparador se solicitará en un único cuadro de diálogo. Hasta que se conceda, el disparador permanecerá activado, pero mostrará un indicador de «Se requiere permiso» y no se activará.

Lo que te ofrece el disparador

Una vez que se activa, tu Flow tiene acceso a:

Campo Ejemplo
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

El propio producto también se incluye como referencia de producto, por lo que puedes añadir condiciones de Flow basadas en el proveedor, el tipo, las etiquetas o la colección antes de modificar ningún precio.

Paso 2: Crear la función

En «Workflow Functions», crea una nueva función y pega el código que aparece a continuación.

Antes de guardar, activa la opción «Necesita datos de Shopify». Esto es lo que permite que tu código acceda a ctx.shopify.graphql(...) para poder leer y modificar los datos de la tienda. A continuación, abre la página «Permisos» en Workflow Functions y concede permiso de escritura sobre los productos, que es el ámbito que necesita esta función para modificar un precio.

Sin ese conmutador, el objeto ctx.shopify no está disponible y la función falla en la primera llamada a la API de administración.

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

Algunas cosas que conviene destacar sobre el código:

  • Acepta un identificador numérico simple o un GID completo. toGid normaliza ambos, por lo que no tienes que preocuparte por el formato que te proporcione Flow.
  • Solo busca el producto principal cuando es necesario. La función productVariantsBulkUpdate requiere un identificador de producto; si no se le pasa uno, la función lo obtiene. Al pasar productId en la entrada, se omite ese intercambio de datos.
  • Comprueba el estado userErrors, no solo los errores de transporte. Una llamada GraphQL puede devolver un código HTTP 200 y, aun así, rechazar la escritura. Esta es la razón más habitual por la que una actualización de precios parece haber tenido éxito, pero no cambia nada.
  • La eliminación de «compare-at» es intencionada. Al pasar compareAtPrice: null o una cadena vacía, se elimina, en lugar de considerarse un error.

Paso 3: Prueba la función antes de conectarla

Utiliza la pestaña «Prueba» con un identificador de variante real de tu tienda. Tómalo de cualquier URL de producto del panel de administración o del historial de eventos del desencadenador.

Establece un precio que te resulte familiar para poder confirmar que el mensaje se ha recibido correctamente.

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

Paso 4: Conexión eléctrica del Flow

Crear un flujo de trabajo en Shopify Flow:

  1. Desencadenante: Cambio en el coste de una variante de producto
  2. Acción: Ejecuta la función desde Workflow Functions y selecciona la función que acabas de crear.

En el campo de entrada JSON, pega esto. Flow sustituye el código Liquid por valores reales cada vez que se ejecuta.

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

Esa es toda la regla de fijación de precios: el precio de venta es cinco veces el coste, y el precio de referencia es siete veces el coste, de modo que el producto siempre muestre un descuento constante.

Esos multiplicadores son un ejemplo, no una recomendación. Cambia times: 5 por el valor que admita realmente tu categoría. Liquid se encarga de los cálculos, por lo que un margen fijo en lugar de un multiplicador sería plus: 12, y un margen porcentual sería times: 1.6.

Si vendes en varias divisas, ten en cuenta que newCost.amount está en la divisa de la tienda. Markets se encarga de la conversión en el momento de la visualización, por lo que no es necesario que lo hagas tú.

Comprueba que funciona de principio a fin

  1. Abre cualquier variante de producto en el panel de administración de Shopify y modifica el coste por unidad.
  2. Guardar
  3. En cuestión de segundos, el precio y el precio de referencia se actualizan automáticamente.

Si no ocurre nada, sigue estos pasos en este orden, ya que cada uno descarta el anterior:

Comprobar Dónde
¿Se disparó el gatillo? Historial de eventos en Workflow Trigger Extensions
¿Se ha ejecutado Flow? Shopify Flow registro de actividades
¿Se ejecutó la función? ¿Y qué se registró en el log? Historial de ejecuciones en Workflow Functions

La causa más habitual de que una ejecución genere un error es que falte un ámbito. ctx.shopify solo está disponible cuando la opción «Needs Shopify data» está activada, y la escritura solo se realiza con éxito una vez que se ha concedido el permiso «write products».

¿A dónde ir ahora?

Este mismo patrón sirve para cualquier aspecto que se vea afectado por un cambio en los costes. Basta con modificar el cuerpo de la función para marcar un producto para su revisión cuando el coste aumente más del 10 por ciento, publicar el margen anterior y el nuevo en Slack o registrar el cambio en un metacampo para la elaboración de informes.

El disparador es la parte que Shopify no te proporciona. Lo que ocurre a continuación es código normal y corriente.

Related articlePermisos y acceso a los datosQué permite el acceso a los datos de inventario y cómo concederlo o revocarlo.Related articleReferencia completa sobre los disparadoresTodos los desencadenantes de Workflow Trigger Extensions, con los campos que incluye cada uno.

Workflow Functions documentación