- Fix console/main.py: import DB_URL instead of missing DB_PATH - Add sqlalchemy text() import for connection test - Replace file-exists check with actual PostgreSQL connection test - Mask password in startup banner - Add docs/METABASE_ACTIONS.md: complete guide for data entry via Metabase Actions (models, forms, dashboard layout, workflows) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
13 KiB
Metabase Actions — Alta de Piezas e Intercambios
Requisitos
- Metabase v0.44+ (Open Source o Pro)
- Actions habilitadas en Admin → Settings
- Database con "Model actions" activado
1. Configuración Inicial
1.1 Habilitar Actions
- Ir a Admin → Settings
- Buscar "Enable Actions" → Activar
- Ir a Admin → Databases → Click en
nexus_autoparts - Activar "Model actions"
1.2 Crear Modelos Base
En Metabase, un Modelo es una pregunta (query) guardada como tabla virtual. Los Actions se vinculan a Modelos.
Modelo 1: Piezas OEM → New → SQL Query:
SELECT
p.id_part,
p.oem_part_number,
p.name_part,
p.name_es,
pg.name_part_group AS grupo,
pc.name_part_category AS categoria,
p.description,
p.description_es,
p.weight_kg,
mat.name_material AS material
FROM parts p
JOIN part_groups pg ON p.group_id = pg.id_part_group
JOIN part_categories pc ON pg.category_id = pc.id_part_category
LEFT JOIN materials mat ON p.id_material = mat.id_material
ORDER BY p.oem_part_number
Guardar → Click ⋯ → Turn into a model → Nombrar: Piezas OEM
Modelo 2: Fitments → New → SQL Query:
SELECT
vp.id_vehicle_part,
b.name_brand AS marca,
m.name_model AS modelo,
y.year_car AS año,
e.name_engine AS motor,
p.oem_part_number,
p.name_part AS pieza,
vp.quantity_required AS cantidad,
pp.name_position_part AS posicion,
vp.fitment_notes AS notas
FROM vehicle_parts vp
JOIN model_year_engine mye ON vp.model_year_engine_id = mye.id_mye
JOIN models m ON mye.model_id = m.id_model
JOIN brands b ON m.brand_id = b.id_brand
JOIN years y ON mye.year_id = y.id_year
JOIN engines e ON mye.engine_id = e.id_engine
JOIN parts p ON vp.part_id = p.id_part
LEFT JOIN position_part pp ON vp.id_position_part = pp.id_position_part
ORDER BY b.name_brand, m.name_model, y.year_car
Guardar → Turn into a model → Nombrar: Fitments
Modelo 3: Aftermarket → New → SQL Query:
SELECT
ap.id_aftermarket_parts,
p.oem_part_number AS oem_ref,
p.name_part AS pieza_oem,
mfr.name_manufacture AS fabricante,
ap.part_number AS numero_aftermarket,
ap.name_aftermarket_parts AS nombre,
ap.name_es AS nombre_es,
qt.name_quality AS calidad,
ap.price_usd AS precio_usd,
ap.warranty_months AS garantia_meses
FROM aftermarket_parts ap
JOIN parts p ON ap.oem_part_id = p.id_part
JOIN manufacturers mfr ON ap.manufacturer_id = mfr.id_manufacture
LEFT JOIN quality_tier qt ON ap.id_quality_tier = qt.id_quality_tier
ORDER BY p.oem_part_number, mfr.name_manufacture
Guardar → Turn into a model → Nombrar: Aftermarket
Modelo 4: Cross-References → New → SQL Query:
SELECT
pcr.id_part_cross_ref,
p.oem_part_number AS oem_ref,
p.name_part AS pieza,
pcr.cross_reference_number AS numero_cruzado,
rt.name_ref_type AS tipo_referencia,
pcr.source_ref AS fuente,
pcr.notes AS notas
FROM part_cross_references pcr
JOIN parts p ON pcr.part_id = p.id_part
LEFT JOIN reference_type rt ON pcr.id_ref_type = rt.id_ref_type
ORDER BY p.oem_part_number, rt.name_ref_type
Guardar → Turn into a model → Nombrar: Cross-References
2. Crear Actions (Formularios de Alta)
Para cada Modelo, crear Actions que permiten insertar datos.
Action 1: Nueva Pieza OEM
- Abrir el modelo Piezas OEM
- Click ⋯ → Info → Actions → New action
- Nombrar:
Alta de Pieza OEM - Pegar este SQL:
INSERT INTO parts (
oem_part_number,
name_part,
name_es,
group_id,
description,
description_es,
weight_kg,
id_material
) VALUES (
{{oem_part_number}},
{{name_part}},
{{name_es}},
{{group_id}},
{{description}},
{{description_es}},
{{weight_kg}},
{{id_material}}
)
- Configurar campos del formulario:
| Variable | Label | Tipo | Requerido |
|---|---|---|---|
oem_part_number |
Número OEM | string | Si |
name_part |
Nombre (EN) | string | Si |
name_es |
Nombre (ES) | string | No |
group_id |
ID Grupo | number | Si |
description |
Descripción (EN) | string (long) | No |
description_es |
Descripción (ES) | string (long) | No |
weight_kg |
Peso (kg) | number | No |
id_material |
ID Material | number | No |
- Click Save
Tip: Para que el usuario no tenga que memorizar
group_id, crea una pregunta auxiliar con los grupos disponibles:SELECT pg.id_part_group AS id, pg.name_part_group AS grupo, pc.name_part_category AS categoria FROM part_groups pg JOIN part_categories pc ON pg.category_id = pc.id_part_category ORDER BY pc.display_order, pg.display_order
Action 2: Nuevo Fitment (vincular pieza a vehículo)
- Abrir el modelo Fitments
- New action → Nombrar:
Alta de Fitment - SQL:
INSERT INTO vehicle_parts (
model_year_engine_id,
part_id,
quantity_required,
id_position_part,
fitment_notes
) VALUES (
{{model_year_engine_id}},
{{part_id}},
{{quantity_required}},
{{id_position_part}},
{{fitment_notes}}
)
| Variable | Label | Tipo | Requerido | Default |
|---|---|---|---|---|
model_year_engine_id |
ID Vehículo (MYE) | number | Si | — |
part_id |
ID Pieza | number | Si | — |
quantity_required |
Cantidad | number | No | 1 |
id_position_part |
Posición (1=front, 2=rear) | number | No | — |
fitment_notes |
Notas | string | No | — |
Tip: Para encontrar el
model_year_engine_id, crea esta pregunta auxiliar:SELECT mye.id_mye AS id, b.name_brand AS marca, m.name_model AS modelo, y.year_car AS año, e.name_engine AS motor FROM model_year_engine mye JOIN models m ON mye.model_id = m.id_model JOIN brands b ON m.brand_id = b.id_brand JOIN years y ON mye.year_id = y.id_year JOIN engines e ON mye.engine_id = e.id_engine WHERE b.name_brand ILIKE {{'%' || marca || '%'}} AND m.name_model ILIKE {{'%' || modelo || '%'}} ORDER BY b.name_brand, m.name_model, y.year_car
Action 3: Fitment Masivo (una pieza → varios vehículos)
- En el modelo Fitments → New action
- Nombrar:
Fitment Masivo por Marca/Modelo/Años - SQL:
INSERT INTO vehicle_parts (model_year_engine_id, part_id, quantity_required, id_position_part)
SELECT mye.id_mye, {{part_id}}, {{quantity_required}}, {{id_position_part}}
FROM model_year_engine mye
JOIN models m ON mye.model_id = m.id_model
JOIN brands b ON m.brand_id = b.id_brand
JOIN years y ON mye.year_id = y.id_year
WHERE b.name_brand ILIKE {{marca}}
AND m.name_model ILIKE {{modelo}}
AND y.year_car BETWEEN {{year_from}} AND {{year_to}}
ON CONFLICT DO NOTHING
| Variable | Label | Tipo | Requerido |
|---|---|---|---|
part_id |
ID Pieza | number | Si |
marca |
Marca (ej: TOYOTA) | string | Si |
modelo |
Modelo (ej: Camry) | string | Si |
year_from |
Año desde | number | Si |
year_to |
Año hasta | number | Si |
quantity_required |
Cantidad | number | Si |
id_position_part |
Posición (1=front, 2=rear, vacío=N/A) | number | No |
Action 4: Nueva Pieza Aftermarket
- En el modelo Aftermarket → New action
- Nombrar:
Alta de Pieza Aftermarket - SQL:
INSERT INTO aftermarket_parts (
oem_part_id,
manufacturer_id,
part_number,
name_aftermarket_parts,
name_es,
id_quality_tier,
price_usd,
warranty_months
) VALUES (
{{oem_part_id}},
{{manufacturer_id}},
{{part_number}},
{{name_aftermarket_parts}},
{{name_es}},
{{id_quality_tier}},
{{price_usd}},
{{warranty_months}}
)
| Variable | Label | Tipo | Requerido |
|---|---|---|---|
oem_part_id |
ID Pieza OEM | number | Si |
manufacturer_id |
ID Fabricante | number | Si |
part_number |
Número Aftermarket | string | Si |
name_aftermarket_parts |
Nombre (EN) | string | No |
name_es |
Nombre (ES) | string | No |
id_quality_tier |
Calidad (1=economy, 2=oem, 3=premium, 4=standard) | number | No |
price_usd |
Precio USD | number | No |
warranty_months |
Garantía (meses) | number | No |
Tip: Pregunta auxiliar para fabricantes:
SELECT id_manufacture AS id, name_manufacture AS fabricante FROM manufacturers ORDER BY name_manufacture
Action 5: Nueva Cross-Reference
- En el modelo Cross-References → New action
- Nombrar:
Alta de Cross-Reference - SQL:
INSERT INTO part_cross_references (
part_id,
cross_reference_number,
id_ref_type,
source_ref,
notes
) VALUES (
{{part_id}},
{{cross_reference_number}},
{{id_ref_type}},
{{source_ref}},
{{notes}}
)
| Variable | Label | Tipo | Requerido |
|---|---|---|---|
part_id |
ID Pieza OEM | number | Si |
cross_reference_number |
Número de Referencia | string | Si |
id_ref_type |
Tipo (1=competitor, 2=interchange, 3=oem_alternate, 4=supersession) | number | No |
source_ref |
Fuente | string | No |
notes |
Notas | string | No |
3. Dashboard de Carga de Datos
Crear un Dashboard que agrupe todo el flujo de carga:
- New → Dashboard → Nombrar:
Panel de Carga de Datos - Agregar estas tarjetas:
Fila 1: Consulta rápida
- Buscar Pieza (pregunta con filtro
{{oem_number}}) - Buscar Vehículo (pregunta con filtros
{{marca}},{{modelo}})
Fila 2: Botones de Actions
- + Nueva Pieza OEM → Action 1
- + Nuevo Fitment → Action 2
- + Fitment Masivo → Action 3
- + Aftermarket → Action 4
- + Cross-Reference → Action 5
Fila 3: Referencias
- Tabla de Grupos (grupos con IDs para referencia)
- Tabla de Fabricantes (fabricantes con IDs)
- Estadísticas (conteos actuales)
Fila 4: Últimos registros
- Últimas piezas cargadas:
SELECT oem_part_number, name_part, name_es, created_at FROM parts ORDER BY created_at DESC LIMIT 10 - Últimos fitments:
SELECT b.name_brand, m.name_model, y.year_car, p.oem_part_number, vp.created_at FROM vehicle_parts vp JOIN model_year_engine mye ON vp.model_year_engine_id = mye.id_mye JOIN models m ON mye.model_id = m.id_model JOIN brands b ON m.brand_id = b.id_brand JOIN years y ON mye.year_id = y.id_year JOIN parts p ON vp.part_id = p.id_part ORDER BY vp.created_at DESC LIMIT 10
4. Flujo de Trabajo Recomendado
Para cargar una pieza nueva con todo su contexto:
1. Abrir "Panel de Carga de Datos"
2. Click [+ Nueva Pieza OEM]
→ Llenar: OEM#, Nombre, Grupo, Descripción
→ Submit → Anotar el ID generado
3. Click [+ Fitment Masivo]
→ Ingresar: ID pieza, Marca, Modelo, Rango de años
→ Submit → La pieza queda vinculada a todos los vehículos
4. Click [+ Aftermarket] (repetir por cada fabricante)
→ Ingresar: ID pieza OEM, ID fabricante, # aftermarket, precio
→ Submit
5. Click [+ Cross-Reference] (repetir por cada referencia)
→ Ingresar: ID pieza, # referencia, tipo
→ Submit
6. Verificar en "Últimos registros" que todo se cargó
5. Preguntas Auxiliares Importantes
Guardar estas como preguntas para tener a mano durante la carga:
Buscar pieza por número
SELECT id_part, oem_part_number, name_part, name_es
FROM parts
WHERE oem_part_number ILIKE {{'%' || numero || '%'}}
OR name_part ILIKE {{'%' || numero || '%'}}
ORDER BY oem_part_number
LIMIT 50
Buscar vehículo por marca/modelo
SELECT mye.id_mye, b.name_brand, m.name_model, y.year_car, e.name_engine
FROM model_year_engine mye
JOIN models m ON mye.model_id = m.id_model
JOIN brands b ON m.brand_id = b.id_brand
JOIN years y ON mye.year_id = y.id_year
JOIN engines e ON mye.engine_id = e.id_engine
WHERE b.name_brand ILIKE {{'%' || marca || '%'}}
AND m.name_model ILIKE {{'%' || modelo || '%'}}
ORDER BY y.year_car DESC
LIMIT 100
Catálogo de grupos (referencia para group_id)
SELECT pg.id_part_group AS id, pg.name_part_group AS grupo,
pc.name_part_category AS categoria
FROM part_groups pg
JOIN part_categories pc ON pg.category_id = pc.id_part_category
ORDER BY pc.display_order, pg.display_order
Catálogo de fabricantes (referencia para manufacturer_id)
SELECT m.id_manufacture AS id, m.name_manufacture AS fabricante,
mt.name_type_manu AS tipo, qt.name_quality AS calidad
FROM manufacturers m
LEFT JOIN manufacture_type mt ON m.id_type_manu = mt.id_type_manu
LEFT JOIN quality_tier qt ON m.id_quality_tier = qt.id_quality_tier
ORDER BY m.name_manufacture
6. IDs de Referencia Rápida
Calidad (id_quality_tier)
| ID | Valor |
|---|---|
| 1 | economy |
| 2 | oem |
| 3 | premium |
| 4 | standard |
Tipo de referencia (id_ref_type)
| ID | Valor | Uso |
|---|---|---|
| 1 | competitor | Número de competidor equivalente |
| 2 | interchange | Intercambio directo compatible |
| 3 | oem_alternate | Número OEM alterno |
| 4 | supersession | Pieza que esta reemplaza |
Posición (id_position_part)
| ID | Valor |
|---|---|
| 1 | front |
| 2 | rear |
Tipo de fabricante (id_type_manu)
| ID | Valor |
|---|---|
| 1 | aftermarket |
| 2 | oem |