FASE 4-5-6: Infraestructura, CRM, Service Orders, Notificaciones, Ahorro, Logistica, API Publica

FASE 4:
- Redis cache de stock con fallback graceful
- Multi-moneda (MXN/USD) con contabilidad en MXN
- Proveedores y ordenes de compra completo
- Meilisearch 1.5M+ partes indexadas
- Metabase KPIs con dashboard auto-generado

FASE 5:
- CRM mejorado: activities, tags, loyalty program, analytics
- Imagenes de partes: upload, resize, thumbnails WebP
- Ordenes de servicio Kanban: received->diagnosis->repair->ready->delivered
- Garantias/RMA, alertas de reorden, multi-sucursal
- Stubs BNPL (APLAZO) y ERP Sync (Aspel/Contpaqi)

FASE 6:
- Notificaciones automaticas: push/WhatsApp/email/in-app
- Reportes de ahorro vs retail_price
- Logistica + tracking: DHL, FedEx, Estafeta, 99min, Uber
- API Publica: API keys, rate limiting, catalog search

Migraciones: v1.9-v3.0
Tests: 93/93 pasando
Backup: nexus_backup_20260427_045859.tar.gz
This commit is contained in:
Nexus Dev
2026-04-27 05:23:30 +00:00
parent b70cb3042b
commit 9ff3dc4c8b
71 changed files with 10939 additions and 420 deletions

View File

@@ -112,8 +112,16 @@ def build_ingreso_xml(sale, tenant_config, customer=None):
if discount_total > 0:
root.set('Descuento', _format_amount(discount_total))
root.set('Moneda', 'MXN')
root.set('Total', _format_amount(sale['total']))
sale_currency = sale.get('currency', 'MXN')
sale_rate = sale.get('exchange_rate', 1.0)
if sale_currency != 'MXN':
# SAT requires MXN; convert and show exchange rate
root.set('Moneda', 'MXN')
root.set('TipoCambio', str(_to_dec(sale_rate).quantize(SIX, ROUND_HALF_UP)))
root.set('Total', _format_amount(_to_dec(sale['total']) * _to_dec(sale_rate)))
else:
root.set('Moneda', 'MXN')
root.set('Total', _format_amount(sale['total']))
root.set('TipoDeComprobante', 'I') # Ingreso
root.set('Exportacion', '01') # No aplica
root.set('MetodoPago', sale.get('metodo_pago_sat', 'PUE'))
@@ -237,8 +245,15 @@ def build_egreso_xml(sale, tenant_config, customer, original_uuid):
if discount_total > 0:
root.set('Descuento', _format_amount(discount_total))
root.set('Moneda', 'MXN')
root.set('Total', _format_amount(sale['total']))
sale_currency = sale.get('currency', 'MXN')
sale_rate = sale.get('exchange_rate', 1.0)
if sale_currency != 'MXN':
root.set('Moneda', 'MXN')
root.set('TipoCambio', str(_to_dec(sale_rate).quantize(SIX, ROUND_HALF_UP)))
root.set('Total', _format_amount(_to_dec(sale['total']) * _to_dec(sale_rate)))
else:
root.set('Moneda', 'MXN')
root.set('Total', _format_amount(sale['total']))
root.set('TipoDeComprobante', 'E') # Egreso
root.set('Exportacion', '01')
root.set('MetodoPago', 'PUE')