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:
@@ -25,22 +25,34 @@ def log_action(conn, action, entity_type=None, entity_id=None,
|
||||
device_id, ip_address, branch_id)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
""", (
|
||||
getattr(g, 'employee_id', None),
|
||||
_safe_g('employee_id'),
|
||||
action,
|
||||
entity_type,
|
||||
entity_id,
|
||||
json.dumps(old_value) if old_value else None,
|
||||
json.dumps(new_value) if new_value else None,
|
||||
getattr(g, 'device_id', None),
|
||||
_safe_g('device_id'),
|
||||
_get_client_ip(),
|
||||
getattr(g, 'branch_id', None),
|
||||
_safe_g('branch_id'),
|
||||
))
|
||||
# Don't commit here — let the caller control the transaction
|
||||
|
||||
|
||||
|
||||
def _safe_g(attr, default=None):
|
||||
"""Safely read flask.g attribute outside of app context."""
|
||||
try:
|
||||
return getattr(g, attr, default)
|
||||
except RuntimeError:
|
||||
return default
|
||||
|
||||
|
||||
def _get_client_ip():
|
||||
"""Get client IP, handling proxies."""
|
||||
from flask import request
|
||||
if request.headers.get('X-Forwarded-For'):
|
||||
return request.headers['X-Forwarded-For'].split(',')[0].strip()
|
||||
return request.remote_addr
|
||||
try:
|
||||
from flask import request
|
||||
if request.headers.get('X-Forwarded-For'):
|
||||
return request.headers['X-Forwarded-For'].split(',')[0].strip()
|
||||
return request.remote_addr
|
||||
except RuntimeError:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user