Saltar al contenido principal
Usa este patrón para mantener Twenty sincronizado con los datos de productos de tu almacén de datos (p. ej., Snowflake, BigQuery, PostgreSQL).

Estructura del flujo de trabajo

  1. Disparador: Según una programación
  2. Code: Consultar tu almacén de datos
  3. Code (opcional): Formatear los datos como array
  4. Iterator: Recorrer cada producto
  5. Upsert Record: Crear o actualizar en Twenty

Paso 1: Programar el disparador

Configura el flujo de trabajo para ejecutarse con una frecuencia acorde a tus necesidades de actualización de datos:
  • Cada 5 minutos para una sincronización casi en tiempo real
  • Cada hora para datos menos críticos
  • Diariamente para actualizaciones por lotes

Paso 2: Consulta tu almacén de datos

Añade una acción Code para obtener datos recientes:
export const main = async () => {
  const intervalMinutes = 10; // Match your schedule frequency
  const cutoffTime = new Date(Date.now() - intervalMinutes * 60 * 1000).toISOString();

  // Replace with your actual data warehouse connection
  const response = await fetch("https://your-warehouse-api.com/query", {
    method: "POST",
    headers: {
      "Authorization": "Bearer YOUR_API_KEY",
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      query: `
        SELECT id, name, sku, price, stock_quantity, updated_at
        FROM products
        WHERE updated_at >= '${cutoffTime}'
      `
    })
  });

  const data = await response.json();
  return { products: data.results };
};
Filtra por updated_at >= last X minutes para recuperar solo los registros cambiados recientemente. Esto mantiene la sincronización eficiente.

Paso 3: Formatear los datos (opcional)

Si tu almacén devuelve datos en un formato que necesita transformación, añade otra acción Code. Las transformaciones comunes incluyen conversiones de tipo, cambio de nombre de campos y limpieza de datos.

Ejemplo: Datos de usuario con campos booleanos y de estado

export const main = async (params: {
  users: any;
}): Promise<object> => {
  const { users } = params;
  const usersFormatted = typeof users === "string" ? JSON.parse(users) : users;

  // Convert string "true"/"false" to actual booleans
  const toBool = (v: any) => v === true || v === "true";

  return {
    users: usersFormatted.map((user) => ({
      ...user,
      activityStatus: String(user.activityStatus).toUpperCase(),
      isActiveLast30d: toBool(user.isActiveLast30d),
      isActiveLast7d: toBool(user.isActiveLast7d),
      isActiveLast24h: toBool(user.isActiveLast24h),
      isTwenty: toBool(user.isTwenty),
    })),
  };
};

Ejemplo: Datos de producto con conversiones de tipo

export const main = async (params: { products: any }) => {
  const products = typeof params.products === "string"
    ? JSON.parse(params.products)
    : params.products;

  return {
    products: products.map(product => ({
      externalId: product.id,
      name: product.name,
      sku: product.sku,
      price: parseFloat(product.price),        // String → Number
      stockQuantity: parseInt(product.stock_quantity),
      isActive: product.status === "active"    // String → Boolean
    }))
  };
};

Ejemplo: Formato de fechas y divisas

export const main = async (params: { deals: any }) => {
  const deals = typeof params.deals === "string"
    ? JSON.parse(params.deals)
    : params.deals;

  return {
    deals: deals.map(deal => ({
      ...deal,
      // Convert Unix timestamp to ISO date
      closedAt: deal.closed_timestamp
        ? new Date(deal.closed_timestamp * 1000).toISOString()
        : null,
      // Ensure amount is a number (remove currency symbols)
      amount: parseFloat(String(deal.amount).replace(/[^0-9.-]/g, "")),
      // Normalize stage names
      stage: deal.stage?.toLowerCase().replace(/_/g, " ")
    }))
  };
};

Transformaciones comunes

Formato de origenFormato de destinoCódigo
"true" / "false"true / falsev === true || v === "true"
"123.45"123.45parseFloat(value)
"active""ACTIVE"value.toUpperCase()
1704067200 (Unix)Fecha ISOnew Date(v * 1000).toISOString()
"$1,234.56"1234.56parseFloat(v.replace(/[^0-9.-]/g, ""))
null / undefined""value || ""

Paso 4: Recorrer los productos

Añade una acción Iterator:
  • Entrada: {{code.products}}
Esto recorre cada producto del array.

Paso 5: Insertar o actualizar cada registro

Dentro del iterador, añade una acción Upsert Record:
ConfiguraciónValor
ObjetoTu objeto de producto personalizado
Coincidir porID externo o SKU (identificador único)
Nombre{{iterator.item.name}}
SKU{{iterator.item.sku}}
Precio{{iterator.item.price}}
Usa Upsert (actualizar o crear) en lugar de construir ramas separadas para crear frente a actualizar. Es más rápido de construir y más fácil de depurar.

Casos de uso de ejemplo

FuenteDatos
Sistema ERPCatálogo de productos, precios, inventario
Plataforma de comercio electrónicoPedidos, clientes, actualizaciones de productos
Almacén de datosMétricas agregadas, datos enriquecidos
Sistema de inventarioNiveles de existencias, alertas de reabastecimiento

Relacionado