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
109 lines
3.4 KiB
Python
109 lines
3.4 KiB
Python
"""Savings Blueprint: retail price management and savings reports.
|
|
|
|
Prefix: /pos/api/savings
|
|
"""
|
|
|
|
from flask import Blueprint, request, jsonify, g
|
|
from middleware import require_auth
|
|
from tenant_db import get_tenant_conn
|
|
from services.savings_engine import (
|
|
get_customer_savings_report, get_global_savings_stats, calculate_item_savings,
|
|
)
|
|
|
|
savings_bp = Blueprint('savings', __name__, url_prefix='/pos/api/savings')
|
|
|
|
|
|
@savings_bp.route('/inventory/<int:item_id>/retail-price', methods=['PUT'])
|
|
@require_auth()
|
|
def set_retail_price(item_id):
|
|
data = request.get_json() or {}
|
|
retail_price = data.get('retail_price')
|
|
if retail_price is None:
|
|
return jsonify({'error': 'retail_price is required'}), 400
|
|
|
|
conn = get_tenant_conn(g.tenant_id)
|
|
try:
|
|
cur = conn.cursor()
|
|
cur.execute("""
|
|
UPDATE inventory SET retail_price = %s WHERE id = %s
|
|
""", (retail_price, item_id))
|
|
conn.commit()
|
|
cur.close()
|
|
return jsonify({'message': 'Retail price updated'})
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
@savings_bp.route('/inventory/bulk-retail-price', methods=['POST'])
|
|
@require_auth()
|
|
def bulk_set_retail_price():
|
|
"""Bulk update retail prices from CSV/JSON.
|
|
|
|
Body: {"items": [{"item_id": 1, "retail_price": 100.00}, ...]}
|
|
"""
|
|
data = request.get_json() or {}
|
|
items = data.get('items', [])
|
|
if not items:
|
|
return jsonify({'error': 'items array is required'}), 400
|
|
|
|
conn = get_tenant_conn(g.tenant_id)
|
|
try:
|
|
cur = conn.cursor()
|
|
updated = 0
|
|
for item in items:
|
|
item_id = item.get('item_id')
|
|
retail_price = item.get('retail_price')
|
|
if item_id and retail_price is not None:
|
|
cur.execute("""
|
|
UPDATE inventory SET retail_price = %s WHERE id = %s
|
|
""", (retail_price, item_id))
|
|
updated += cur.rowcount
|
|
conn.commit()
|
|
cur.close()
|
|
return jsonify({'updated': updated, 'message': f'{updated} prices updated'})
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
@savings_bp.route('/customers/<int:customer_id>', methods=['GET'])
|
|
@require_auth()
|
|
def customer_savings(customer_id):
|
|
months = int(request.args.get('months', 12))
|
|
conn = get_tenant_conn(g.tenant_id)
|
|
try:
|
|
report = get_customer_savings_report(conn, customer_id, months=months)
|
|
return jsonify(report)
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
@savings_bp.route('/stats', methods=['GET'])
|
|
@require_auth()
|
|
def global_savings():
|
|
from_date = request.args.get('from_date')
|
|
to_date = request.args.get('to_date')
|
|
conn = get_tenant_conn(g.tenant_id)
|
|
try:
|
|
stats = get_global_savings_stats(conn, g.tenant_id, from_date=from_date, to_date=to_date)
|
|
return jsonify(stats)
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
@savings_bp.route('/calculate', methods=['POST'])
|
|
@require_auth()
|
|
def calculate_savings():
|
|
"""Calculate savings for a given price vs retail."""
|
|
data = request.get_json() or {}
|
|
unit_price = float(data.get('unit_price', 0))
|
|
retail_price = float(data.get('retail_price', 0))
|
|
quantity = int(data.get('quantity', 1))
|
|
savings, pct = calculate_item_savings(unit_price, retail_price, quantity)
|
|
return jsonify({
|
|
'unit_price': unit_price,
|
|
'retail_price': retail_price,
|
|
'quantity': quantity,
|
|
'savings_amount': savings,
|
|
'savings_percentage': pct,
|
|
})
|