+server.js
4.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
import { API_HOST } from '$lib/api.js';
const API_BASE = API_HOST + '/api/rubro';
let cachedClasificador = null;
let cachedEstados = null;
let cachedTimestamp = 0;
const CACHE_TTL = 5 * 60 * 1000;
function getParent(code, nivel) {
const s = String(code).padStart(5, '0');
if (nivel === 'tipo') return null;
// clase (ABC00) → tipo (AB000)
if (nivel === 'clase') return s.substring(0, 2) + '000';
// cuenta (ABCD0) → clase (ABC00)
if (nivel === 'cuenta') return s.substring(0, 3) + '00';
// sub_cuenta (ABCDE) → cuenta (ABCD0)
if (nivel === 'sub_cuenta') return s.substring(0, 4) + '0';
return null;
}
async function loadAndCacheAll() {
const now = Date.now();
if (cachedEstados && (now - cachedTimestamp) < CACHE_TTL) return;
const clasRes = await fetch(`${API_BASE}/clasificador`);
if (!clasRes.ok) throw new Error('Failed to fetch clasificador');
cachedClasificador = await clasRes.json();
const estados = new Map();
const batchSize = 80;
for (let i = 0; i < cachedClasificador.length; i += batchSize) {
const batch = cachedClasificador.slice(i, i + batchSize);
const results = await Promise.all(batch.map(async (item) => {
try {
const code = item.rubro;
const res = await fetch(`${API_BASE}/${code}`);
if (!res.ok) return null;
const data = await res.json();
return { code: String(code), estados: data.estados || [] };
} catch { return null; }
}));
results.filter(Boolean).forEach(r => estados.set(r.code, r.estados));
}
cachedEstados = estados;
cachedTimestamp = now;
}
export async function GET({ url }) {
const gestion = parseInt(url.searchParams.get('gestion') || '2025');
const entidad = url.searchParams.get('entidad') || '0';
try {
if (entidad === '0') {
await loadAndCacheAll();
const results = [];
for (const item of cachedClasificador) {
const code = String(item.rubro);
const estados = cachedEstados.get(code);
if (!estados) continue;
const yearData = estados.find(d => d.gestion === gestion);
if (!yearData || !yearData.total) continue;
results.push({
gestion,
nivel: item.nivel || yearData.nivel || '',
rubro: code,
desc_rubro: item.desc_rubro || '',
parent: getParent(code, item.nivel || yearData.nivel),
devengado: yearData.total
});
}
// Add missing parent nodes (tipos without estados)
const ids = new Set(results.map(r => r.rubro));
const missingParents = new Set();
results.forEach(r => {
if (r.parent && !ids.has(r.parent)) missingParents.add(r.parent);
});
for (const parentCode of missingParents) {
const item = cachedClasificador.find(c => String(c.rubro) === parentCode);
results.push({
gestion,
nivel: item?.nivel || 'tipo',
rubro: parentCode,
desc_rubro: item?.desc_rubro || parentCode,
parent: getParent(parentCode, item?.nivel || 'tipo'),
devengado: 0
});
}
return new Response(JSON.stringify(results), {
headers: { 'Content-Type': 'application/json' }
});
} else {
if (!cachedClasificador) {
const clasRes = await fetch(`${API_BASE}/clasificador`);
cachedClasificador = await clasRes.json();
}
const results = [];
const batchSize = 80;
for (let i = 0; i < cachedClasificador.length; i += batchSize) {
const batch = cachedClasificador.slice(i, i + batchSize);
const batchResults = await Promise.all(batch.map(async (item) => {
try {
const code = String(item.rubro);
const res = await fetch(`${API_BASE}/${code}/entidades/${entidad}`);
if (!res.ok) return null;
const data = await res.json();
const yearData = data.find(d => d.gestion === gestion);
if (!yearData || !yearData.monto) return null;
return {
gestion,
nivel: item.nivel || '',
rubro: code,
desc_rubro: item.desc_rubro || '',
parent: getParent(code, item.nivel),
devengado: yearData.monto
};
} catch { return null; }
}));
results.push(...batchResults.filter(Boolean));
}
return new Response(JSON.stringify(results), {
headers: { 'Content-Type': 'application/json' }
});
}
} catch (err) {
return new Response(JSON.stringify({ error: 'Failed to build treemap' }), {
status: 500, headers: { 'Content-Type': 'application/json' }
});
}
}