> ## Documentation Index
> Fetch the complete documentation index at: https://docs.twenty.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Incorporar datos de productos en Twenty

> Sincroniza los datos del catálogo de productos desde un almacén de datos a tu CRM según una programación.

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

<img src="https://mintcdn.com/twenty/ZVHVYG6uDKpO6cME/images/user-guide/workflows/create_users_workflow.png?fit=max&auto=format&n=ZVHVYG6uDKpO6cME&q=85&s=8542bb62659bb0e7d1de08c6f36d83d5" style={{width:'100%'}} width="4550" height="7720" data-path="images/user-guide/workflows/create_users_workflow.png" />

## 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:

```javascript theme={null}
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 };
};
```

<Tip>
  Filtra por `updated_at >= last X minutes` para recuperar solo los registros cambiados recientemente. Esto mantiene la sincronización eficiente.
</Tip>

## 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

```javascript theme={null}
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

```javascript theme={null}
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

```javascript theme={null}
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 origen    | Formato de destino | Código                                   |
| -------------------- | ------------------ | ---------------------------------------- |
| `"true"` / `"false"` | `true` / `false`   | `v === true \|\| v === "true"`           |
| `"123.45"`           | `123.45`           | `parseFloat(value)`                      |
| `"active"`           | `"ACTIVE"`         | `value.toUpperCase()`                    |
| `1704067200` (Unix)  | Fecha ISO          | `new Date(v * 1000).toISOString()`       |
| `"$1,234.56"`        | `1234.56`          | `parseFloat(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ón     | Valor                                  |
| ----------------- | -------------------------------------- |
| **Objeto**        | Tu objeto de producto personalizado    |
| **Coincidir por** | ID externo o SKU (identificador único) |
| **Nombre**        | `{{iterator.item.name}}`               |
| **SKU**           | `{{iterator.item.sku}}`                |
| **Precio**        | `{{iterator.item.price}}`              |

<Tip>
  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.
</Tip>

## Casos de uso de ejemplo

| Fuente                                 | Datos                                               |
| -------------------------------------- | --------------------------------------------------- |
| **Sistema ERP**                        | Catálogo de productos, precios, inventario          |
| **Plataforma de comercio electrónico** | Pedidos, clientes, actualizaciones de productos     |
| **Almacén de datos**                   | Métricas agregadas, datos enriquecidos              |
| **Sistema de inventario**              | Niveles de existencias, alertas de reabastecimiento |

## Relacionado

* [Disparadores de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-triggers)
* [Acciones de flujos de trabajo](/l/es/user-guide/workflows/capabilities/workflow-actions)
* [Manejar arreglos en acciones de código](/l/es/user-guide/workflows/how-tos/advanced-configurations/handle-arrays-in-code-actions)
