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
- Disparador: Según una programación
- Code: Consultar tu almacén de datos
- Code (opcional): Formatear los datos como array
- Iterator: Recorrer cada producto
- 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.
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
}))
};
};
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, " ")
}))
};
};
| 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}} |
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
| 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