Files
Autoparts-DB/docs/plans/2026-10-11-plan-maestro-mejoras.md
consultoria-as a386ca5b4a
Some checks failed
CI / lint-and-test (3.11) (push) Has been cancelled
CI / lint-and-test (3.13) (push) Has been cancelled
Mejoras de sesion: estabilizacion, IA, Nexus Yonke y marketplace B2B
- F0 blindaje: Redis/Celery, pg_hba sin trust LAN, secretos a env 600, backups, webhook auth, jobs muertos
- F1 caja: bugs POS, reembolsos al corte, apartados, CFDI, topes de descuento en cotizacion/apartado
- F2/F7 IA de catalogo: autocategorizacion, dedup de SKUs con embeddings, OCR de facturas, descripciones, claves SAT, limpieza de fitment
- F3 IA en el flujo: vision Yonke, preguntas MeLi, venta cruzada, anomalias, sinonimos, busqueda semantica (rerank/embeddings)
- F4/F8 escala y cartera: /metrics+Prometheus+Grafana, pool, score de credito, churn, metricas multi-tenant
- Nexus Yonke (M1-M6): modelo de datos 1-de-1, alta por donador, vitrina publica, MeLi usado, consignacion, garantia por dias, reportes, etiquetas
- Marketplace B2B entre yonkes (catalogo + pedidos cross-tenant)
- Seguridad: auth en /api/admin/* (35 endpoints), alertas por tenant
- Migraciones v4.27/v4.28; timers expire-demos y rollup-metrics
2026-10-12 00:57:31 +00:00

18 KiB
Raw Blame History

Plan Maestro de Mejoras — Nexus Autoparts

Fecha: 11 Oct 2026 · Base: auditoría completa del sistema (6 investigaciones con evidencia en archivo:línea, SQL y curl) Énfasis: MercadoLibre (eje comercial) + apertura del mercado de partes usadas (yonkes/deshuesaderos)


0. Resumen ejecutivo

El sistema es un esqueleto potente con el ciclo comercial roto en los últimos metros. Lo que funciona bien es la base (motor de ventas con locks y partida doble, aislamiento multi-tenant, compatibilidad vehicular, OAuth de MeLi con refresh persistido, WA CRM recién instalado). Lo que está roto es justo lo que genera ingresos:

# Bloqueante Impacto Evidencia
1 Backups caídos desde el 1-oct Sin respaldo fresco de ningún tenant (riesgo existencial) scripts/backup_selective.sh:299-303 — ((deleted++)) devuelve 1 y mata el script bajo set -euo pipefail
2 Alta de tenants falla No se puede onboardear ningún cliente nuevo tenant_manager.py:201-211 aplica v1.2_subdomain.sql (migración de master) al tenant → rollback
3 CFDI 100% roto Ninguna factura se timbra invoicing_bp.py:65 — SELECT … codigo_postal FROM branches (la columna es cp)
4 Órdenes MeLi nunca se convierten a venta 31/31 órdenes de Rached sin vender en el POS marketplace_external_service.py:1619 — inventory_record_sale(..., reference=) no existe en la firma
5 Publicar en MeLi desde el POS está muerto Validar/Publicar no hacen nada (ReferenceError) inventory.js:2069,2135 llaman _collectMeliCustomData(), que no existe
6 Sync de stock MeLi falla 52/52 Envía price:0 a ML cada 15 min (0 éxitos desde junio) tasks.py:135-145 — listings con inventory_id NULL
7 Webhook MeLi nunca procesa Sin notificaciones en tiempo real, público y sin firma marketplace_external_bp.py:775-790 — consulta tenant_config en la BD maestra (no existe ahí)
8 Catálogo público con 500s El visitante nuevo ve error en buscador, categorías y detalle dashboard/server.py:977,1281,1994 contra tablas TecDoc retiradas
9 Plan demo → fallback free Estrada (157,243 productos) bloqueada en 100 productos billing.py:25-28 + tenants.plan='demo'
10 /api/admin/tenants* sin autenticación Cualquiera en internet lista y modifica módulos de tenants dashboard/server.py:4660,4674,4691

Y el hallazgo estratégico: el negocio de partes usadas es hoy imposible de operar en el sistema (no existe condición, ni pieza única, ni vehículo donador, ni multi-foto) — y MeLi publica todo como "condition":"new" hardcodeado. Abrir ese mercado requiere un cambio de modelo de datos, no cosmético.


1. MercadoLibre — cerrar el ciclo antes de soñar en grande

1.1 Fase A — Desbloquear lo que ya está construido (1 semana)

Tarea Detalle Esfuerzo
A1 Implementar _collectMeliCustomData(), _pollMeliAsync(), _renderPublishResult() en inventory.js (los inputs de título/precio/stock y atributos ya se generan pero nunca se envían) y mandar custom_data al backend (marketplace_external_service.py:669-673) M
A2 Corregir convert_order_to_sale: usar record_sale(conn, inventory_id, branch_id, quantity, sale_id=…) — hoy TypeError → rollback → 31 órdenes sin convertir (service.py:1619, inventory_engine.py:199) S
A3 import_existing_listings: poblar inventory_id por seller_custom_field/part_number/alias y saltar el push cuando price<=0; el ON CONFLICT no aplica con NULL (service.py:1785) S
A4 Webhook: resolver tenant por meli_user_id consultando cada tenant_config de tenant (no la maestra), validar X-Signature con validate_webhook_signature (ya existe, nunca se llama) y procesar el resource correcto (no un sync completo) M
A5 Normalizar status de preguntas (UPPER) — hoy 4 preguntas reales no se pueden responder por comparar 'unanswered' vs UNANSWERED (marketplace_external.js:516) S
A6 Pasar tenant_conn a _get_meli_service en sync_meli_stock_price_task (tasks.py:112) para no perder el refresh token rotado S
A7 Pausar/reactivar automáticamente por stock (0 → pausar, >0 → reactivar) en la cola meli_sync_queue + drenarla por timer M
A8 Sincronizar órdenes en todos los estados (cancelled, payment_required…) y corregir buyer_nickname (ML ya no manda from.nickname) S
A9 Corregir el estimador de comisiones por artículo (busca #meliFee-<id> en un DOM que nunca lo crea, inventory.js:2295) S
A10 Definir/otorgar el permiso marketplace.manage (no existe en ningún tenant) y proteger los GET del módulo S

Resultado: el ciclo MeLi→POS (publicar → vender → convertir → facturar) cierra por primera vez.

1.2 Fase B — Publicar en volumen (2-3 semanas) ← el verdadero cuello de botella

Tarea Detalle Esfuerzo
B1 Pipeline masivo de imágenes — el bloqueo real: 0 imágenes en 6,851 SKUs de Rached, 0 en 14,568 de Capi, 1 en 157,243 de Estrada. ML exige imagen. Usar los servicios de scraping existentes (services/image_scraper.py, image_service.py) + una cola por lotes con revisión L
B2 Selección y publicación por lotes real: hoy la UI solo selecciona una página de 50 y un solo category_id para todo el lote. Agregar: seleccionar todo el resultado filtrado, categorización asistida por IA (nombre+marca→categoría ML con domain_discovery), y confirmación por grupos M
B3 Rate-limit/backoff en el worker de publicación (Celery) con reintentos y progreso visible (barra + reporte de fallidos por motivo) M
B4 Ciclo export→edición→reimportación para fitment e imágenes (hoy el export no incluye compat ni image_url) M

1.3 Fase C — Vender más (3-4 semanas)

Oportunidad Hoy Qué agregar
Variaciones Nada (0 referencias a variations) Agrupar SKUs en una publicación con variations/attribute_combinations (mismo producto, varias medidas)
Catálogo de MeLi Solo category_id Detectar categorías que exigen catálogo (catalog_listing, catalog_product_id), avisar en la UI y usar price_to_win para competir por la oferta ganadora
Promociones Nada Oferta del día, descuentos por volumen, sale_price por publicación
Envíos Solo me2/custom, sin etiquetas Flex, generación de etiqueta, mark_ready_to_ship (existe y nunca se usa), tracking
Preguntas Manual y roto Auto-respuesta con ai_chat.py + plantillas por categoría + SLA visible
Reputación Nada Panel con /users/{id}/reputation: nivel, reclamos, cancelaciones, tiempo de despacho
Rentabilidad por publicación Solo el estimado al publicar (no se guarda) Persistir comisión + envío + costo en marketplace_listings y panel de margen real por SKU
Facturación de ventas MeLi Ningún enganche a CFDI Encolar CFDI automático al convertir orden (con RFC del comprador si lo pide)
Cancelaciones/reintegros/claims Solo estado local Llamar a la API de ML (cancelar, devoluciones, mediaciones) con SLA
Multi-cuenta 1 cuenta MeLi por tenant Varias cuentas MeLi por tenant (varias marcas) con selección por publicación
Panel comercial Nada Ventas MeLi vs POS, conversión, SKUs sin stock con publicación activa, top ventas, alertas de publicaciones caídas

1.4 Fase D — Robustez operativa

  • Onboarding asistido de credenciales MeLi (hoy es manual y el fallo típico —ACCA— es un client_secret regenerado que nadie detecta hasta que el cliente llama; ya mejoramos el mensaje, falta un chequeo proactivo diario que avise antes).
  • Versionar el timer de sync en el repo (hoy vive solo en /etc/systemd/system).
  • Pruebas automatizadas del módulo (hoy: cero tests de MeLi).

2. Abrir el mercado: negocios de SOLO partes usadas (yonkes/deshuesaderos)

2.1 Por qué hoy no se puede (evidencia)

Requisito del yonke Estado Dónde se rompe
Pieza única (1-de-1, sin código de parte) ❌ part_number es NOT NULL + UNIQUE global (v4.0_multi_branch.sql:214); dos espejos iguales de donadores distintos se fusionan en el import
Saber de qué auto salió (donador) ❌ No hay donor_vin/donor_mileage; la compat existente es "sirve para", no "salió de"
Condición nuevo/usado/A/B/C ❌ 0 columnas de condición en todo el esquema; MeLi publica "condition":"new" hardcodeado
Fotos múltiples (ángulos + daños) ❌ Un solo image_url varchar; cada subida reemplaza la anterior (inventory_bp.py:995)
Garantía por días (15/30 días) ⚠️ Solo warranty_months (mínimo 1 mes)
Buscar "todo lo que tengo de un Civic 2015" ❌ La navegación por vehículo exige part_number para poblar compat
Consignación / piezas de terceros ❌ No existe modelo de propiedad; todo el stock se asume propio
Valuación con costo 0 ⚠️ Vender funciona, pero los reportes de valoración/ABC dan $0 (inventory_bp.py:1879)

2.2 Plan de producto: "Nexus Yonke" (5-6 semanas)

M1 — Modelo de datos (1 semana)

  • inventory: condition (nuevo|usado|reconstruido), condition_grade (A/B/C/D), is_unique_piece, serial_number, donor_vin, donor_make/model/year, donor_mileage_km, ownership (propio|consigna), acquisition_cost.
  • Relajar unicidad: índice único parcial WHERE part_number IS NOT NULL + auto-SKU para piezas únicas (USADO-<VIN-corto>-<secuencia>), manteniendo el flujo de partes nuevas intacto.
  • Nueva tabla inventory_images (inventory_id, url, order, is_primary) con migración del image_url actual.
  • warranties: warranty_days + política por defecto de usados en tenant_config.
  • Nuevas tablas consignments / consignment_items (tercero, pieza, precio pactado, comisión, estado: en_consigna/vendida/devuelta/liquidada).

M2 — Alta rápida en mostrador (1-2 semanas)

  • Flujo "Registrar pieza usada": foto múltiple (cámara del tablet) + VIN del donador (el decodificador NHTSA ya existe: /pos/api/catalog/vin) + pieza + precio + condición. Objetivo: < 30 segundos por pieza.
  • Ingreso de donador completo: registrar el auto (VIN, km, color) y generar N piezas colgadas de ese donador con un solo flujo (hoy: pieza por pieza).
  • Etiqueta propia por pieza única (código de barras ya implementado) para control físico en el patio.

M3 — Búsqueda por donador (1 semana)

  • Filtro/búsqueda por vehículo donador (marca/modelo/año/color/km) sin requerir part_number; indexar esos campos en Meilisearch.
  • Buscador "¿qué tengo de este auto?" + "¿qué tengo compatible con este auto?" (compat comprador, ya existe) como dos pestañas distintas.

M4 — MeLi para usados (1 semana)

  • condition dinámico en el payload (usado/remanufacturado) + grado en el título/atributos.
  • Mapeo de categorías ML para usados (ML tiene categorías de autopartes usadas con reglas propias).
  • Fotos múltiples al anuncio (ML acepta hasta 10; hoy se manda 1).

M5 — Consignación y garantía (1-2 semanas)

  • Liquidación a consignantes (reporte por periodo, comisión pactada, estado de cuenta del consignante).
  • Garantía de usados por días con política por defecto y comprobante impreso.
  • Reportes del segmento: rotación por donador, piezas sin foto, días en patio, margen por pieza.

M6 — Plan y onboarding comercial (3-4 días)

  • Plan "Yonke" (precio menor): sin catálogo TecDoc, límites amplios, módulos ajustados, onboarding sin importación de catálogo nuevo (alta por escaneo/foto).
  • Landing y material para el segmento (hoy grep yonke|usadas en docs/pitch → 0 resultados).
  • Marketplace B2B entre yonkes (is_seller, hoy nunca activado en ningún tenant) como diferenciador: intercambio de piezas usadas entre deshuesaderos de la red.

3. Núcleo comercial y fiscal

CFDI (crítico):

  • Corregir codigo_postal → cp (invoicing_bp.py:65) — desbloquea ingreso, egreso, PDF y factura global.
  • Complementos de pago (REP) para PPD: build_pago_payload existe y nunca se invoca; hoy toda venta a crédito incumple SAT.
  • Subir CSD por sucursal (hoy has_csd:false → ni timbrando funcionaría).
  • Devoluciones: encolar CFDI egreso automático + reembolso trazado en caja.
  • Moneda/tipo de cambio en facturación (invoicing_bp.py:83 no lee currency).

Contabilidad:

  • Pólizas para abonos de clientes, cortes de caja, devoluciones y apartados (4 funciones de accounting_engine existen y nunca se llaman).
  • record_return_entry (no existe).
  • Dejar de perder asientos en silencio: los hooks try/except: pass deben loggear (ya se perdió 1 de 18 ventas sin aviso).
  • Exportables para el contador: pólizas, balanza, estado de resultados, DIOT (hoy solo antigüedad de saldos).
  • CxP real con tabla de pagos a proveedores (paid = 0 # TODO).
  • Caja: pagos mixtos no suman al efectivo esperado; apartados duplican el efectivo en el corte.

4. Catálogo, búsqueda y datos

  1. Arreglar el catálogo público (visitante nuevo ve 500): /api/catalog/search, /api/search, /api/categories, modo OEM y detalle de parte. Modo local por defecto + degradación elegante (el POS ya tiene el guard _oem_blocked, el dashboard no).
  2. Conectar Meilisearch de verdad: los índices nexus_inventory_* ya están poblados y actualizados a diario, ningún código los consulta; la búsqueda local hace seq-scan de 157k filas (175 ms).
  3. Calidad de fitment: el fitment QWEN generó 20.5M filas ruidosas (Corolla 2012 → 411,685 "compatibles" ≈ 40% del catálogo). Política de confianza por fuente + validación + priorizar la compat de proveedor (limpia, 29,760 MYEs). Decidir el destino del catálogo maestro (los 15 GB de JSON TecDoc siguen en disco).
  4. Imágenes masivas (bloquea catálogo público y MeLi) — mismo pipeline de B1.
  5. Import/export round-trip con compat, imágenes y min/max/reorder.
  6. Import con vehículo roto: bulk-import devuelve 500 si el CSV trae make/model/year (consulta tablas de vehículos en la conexión del tenant) — justo el flujo de "para qué carro sirve".
  7. Multi-sucursal real: todo el stock vive en la sucursal 1 y inventory.branch_id es NULL; transferencias/conteos/reorden implementados y sin uso (reorden además filtra por la columna equivocada y reorder_point es NULL al 100%).
  8. Etiquetas: código de barras generado pero sin endpoint de impresión de etiquetas (solo tickets).

5. SaaS, onboarding y escala

Tema Estado Acción
Alta de tenants ❌ Rota (v1.2/v1.6 de master en el runner de tenants) Marcar como master-only/mover al runner master
Template de tenant Congelado en 32 tablas (le faltan ~56) Refrescar template en cada alta o eliminarlo del flujo
Planes demo/standard no existen en PLANS → fallback a límites de free Agregar planes reales o mapear; aplicar la expiración de demos
Fuga de ingresos Upgrade a enterprise gratis desde el POS + demos vencidas sin efecto + subscriptions desincronizada Quitar el autoservicio, sincronizar tenants.plan↔subscriptions.plan, job diario de expiración
Seguridad multi-tenant /api/admin/tenants* del dashboard sin auth Proteger + quitar ?tenant= de la superficie pública
Módulos Dos nomenclaturas (module_* vs *_enabled) con defaults contradictorios Unificar y sembrar en el template
Drift de esquema v3.5_whatsapp_state_machine marcada SKIP → 6 tenants sin wa_customer_links/wa_learning_sessions → el chatbot WhatsApp falla en esos tenants Aplicar la migración real
Bases huérfanas 6 DBs tenant_* sin fila en tenants (~84 MB) Limpiar
Reset de demo No limpia clientes ni conversaciones (filtra PII entre prospectos) Completar el truncado
Escala a 100 tenants Pool 2-20 conexiones × 8 workers × 100 tenants > límite de Postgres; todo con superusuario PgBouncer + roles por tenant + métricas por tenant
Secretos POS_JWT_SECRET/API keys en unit files legibles por grupo EnvironmentFile root-only + rotación

6. Roadmap propuesto (6 sprints)

Sprint Foco Entregable verificable
S1 — Estabilizar (1 sem) Backups (fix ((deleted++)) + verificar drill), alta de tenants, auth de /api/admin/tenants*, plan demo, expiración de demos Respaldo fresco de 9 bases verificado con restore; un tenant nuevo creado de punta a punta
S2 — MeLi cierra el ciclo (1 sem) Fase A completa (10 tareas) Publicar 1 SKU desde el POS → vender en ML → orden convertida a venta → CFDI encolado
S3 — Volumen MeLi (2 sem) Imágenes masivas + lotes + rate-limit + export/import con compat 200+ SKUs publicados sin intervención manual, con progreso y reporte de fallos
S4 — Yonke MVP (2-3 sem) M1+M2+M3 (modelo, alta rápida por donador, búsqueda por donador) Un deshuesadero de prueba carga 50 piezas usadas con foto y VIN, y las encuentra por vehículo
S5 — Fiscal y contabilidad (2 sem) CFDI (cp, REP, CSD, egresos), pólizas faltantes, caja (mixtos/apartados), exportables contador Factura timbrada real; estado de resultados cuadra; DIOT exportable
S6 — Vender más + abrir mercado (2-3 sem) MeLi Fase C (variaciones, catálogo, reputación, rentabilidad, preguntas IA) + Yonke M4-M6 (ML usados, consignación, plan Yonke) Panel de rentabilidad por publicación; primera pieza usada publicada en ML como used

7. Métricas de éxito

  • Confiabilidad: 30 días consecutivos con respaldo verificado; 0 tenants con esquema incompleto; 0 endpoints 500 en el barrido automatizado.
  • MercadoLibre: conversión de órdenes → venta en el POS (hoy 0%, meta 100%); publicaciones sincronizadas sin error (hoy 0/52, meta 100%); ≥200 SKUs publicados en volumen; % de órdenes facturadas automáticamente.
  • Yonke: tiempo de alta de una pieza usada < 30 s; 100% de piezas usadas con foto y VIN de donador; búsqueda por donador con resultados < 500 ms; 1 cliente del segmento en producción.
  • Comercial: tenants de pago con plan correcto aplicado (hoy 3 con plan desincronizado), fin de la fuga por autoservicio, ≥1 cliente nuevo onboardeado sin intervención manual.