diff --git a/pos/blueprints/accounting_bp.py b/pos/blueprints/accounting_bp.py
index 7533925..99e08cb 100644
--- a/pos/blueprints/accounting_bp.py
+++ b/pos/blueprints/accounting_bp.py
@@ -687,6 +687,73 @@ def aging_report():
return jsonify({'data': rows, 'totals': totals})
+@accounting_bp.route('/aging-summary', methods=['GET'])
+@require_auth('accounting.view')
+def aging_summary():
+ """Customer-level accounts receivable summary (used by reports)."""
+ conn = get_tenant_conn(g.tenant_id)
+ cur = conn.cursor()
+
+ cur.execute("""
+ SELECT c.id, c.name, c.rfc, c.credit_limit, c.credit_balance,
+ s.id as sale_id, s.total, s.created_at,
+ EXTRACT(DAY FROM NOW() - s.created_at)::int as days_outstanding
+ FROM customers c
+ JOIN sales s ON s.customer_id = c.id
+ WHERE s.sale_type = 'credit'
+ AND s.status = 'completed'
+ AND c.credit_balance > 0
+ ORDER BY c.name, s.created_at
+ """)
+
+ customers = {}
+ for r in cur.fetchall():
+ cust_id = r[0]
+ if cust_id not in customers:
+ customers[cust_id] = {
+ 'id': r[0], 'name': r[1], 'rfc': r[2],
+ 'credit_limit': float(r[3]) if r[3] else 0,
+ 'credit_balance': float(r[4]) if r[4] else 0,
+ 'corriente': 0, 'd1_30': 0, 'd31_60': 0, 'd61_90': 0, 'd90_plus': 0,
+ 'total': 0,
+ }
+
+ amount = float(r[6]) if r[6] else 0
+ days = r[8] or 0
+
+ if days <= 0:
+ customers[cust_id]['corriente'] += amount
+ elif days <= 30:
+ customers[cust_id]['d1_30'] += amount
+ elif days <= 60:
+ customers[cust_id]['d31_60'] += amount
+ elif days <= 90:
+ customers[cust_id]['d61_90'] += amount
+ else:
+ customers[cust_id]['d90_plus'] += amount
+
+ customers[cust_id]['total'] += amount
+
+ result = list(customers.values())
+ for c in result:
+ for key in ('corriente', 'd1_30', 'd31_60', 'd61_90', 'd90_plus', 'total'):
+ c[key] = round(c[key], 2)
+
+ totals = {
+ 'corriente': round(sum(c['corriente'] for c in result), 2),
+ 'd1_30': round(sum(c['d1_30'] for c in result), 2),
+ 'd31_60': round(sum(c['d31_60'] for c in result), 2),
+ 'd61_90': round(sum(c['d61_90'] for c in result), 2),
+ 'd90_plus': round(sum(c['d90_plus'] for c in result), 2),
+ 'total': round(sum(c['total'] for c in result), 2),
+ 'count': len(result),
+ }
+
+ cur.close()
+ conn.close()
+ return jsonify({'data': result, 'totals': totals})
+
+
# ─── Fiscal Periods ────────────────────────────────
@accounting_bp.route('/periods', methods=['GET'])
diff --git a/pos/static/js/reports.js b/pos/static/js/reports.js
index 4b9cf3e..117cc70 100644
--- a/pos/static/js/reports.js
+++ b/pos/static/js/reports.js
@@ -504,7 +504,7 @@ const Reports = (() => {
agingEl.innerHTML = spinner();
try {
- var data = await apiFetch('/pos/api/accounting/aging');
+ var data = await apiFetch('/pos/api/accounting/aging-summary');
var clients = data.data || [];
var totals = data.totals || {};
diff --git a/pos/static/pwa/sw.js b/pos/static/pwa/sw.js
index 853caae..834d624 100644
--- a/pos/static/pwa/sw.js
+++ b/pos/static/pwa/sw.js
@@ -6,7 +6,7 @@
// The fetch handler normalizes static asset URLs (strips ?v= query strings)
// so templates can use cache-busting query params freely.
-const CACHE_NAME = 'nexus-pos-v25';
+const CACHE_NAME = 'nexus-pos-v26';
const APP_SHELL = [
'/pos/static/css/tokens.css',
diff --git a/pos/templates/reports.html b/pos/templates/reports.html
index b38a510..beefd2a 100644
--- a/pos/templates/reports.html
+++ b/pos/templates/reports.html
@@ -371,7 +371,7 @@
-
+