Rafael Lopez

vista diaria

......@@ -8,9 +8,11 @@
"name": "ppto",
"version": "0.0.1",
"dependencies": {
"@observablehq/plot": "^0.6.17",
"@supabase/supabase-js": "^2.98.0",
"d3": "^7.9.0",
"fuse.js": "^7.1.0",
"hyparquet": "^1.25.1",
"marked": "^17.0.4"
},
"devDependencies": {
......@@ -515,6 +517,20 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@observablehq/plot": {
"version": "0.6.17",
"resolved": "https://registry.npmjs.org/@observablehq/plot/-/plot-0.6.17.tgz",
"integrity": "sha512-/qaXP/7mc4MUS0s4cPPFASDRjtsWp85/TbfsciqDgU1HwYixbSbbytNuInD8AcTYC3xaxACgVX06agdfQy9W+g==",
"license": "ISC",
"dependencies": {
"d3": "^7.9.0",
"interval-tree-1d": "^1.0.0",
"isoformat": "^0.2.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@polka/url": {
"version": "1.0.0-next.29",
"resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
......@@ -1410,6 +1426,12 @@
"node": ">= 0.4"
}
},
"node_modules/binary-search-bounds": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-2.0.5.tgz",
"integrity": "sha512-H0ea4Fd3lS1+sTEB2TgcLoK21lLhwEJzlQv3IN47pJS976Gx4zoWe0ak3q+uYh60ppQxg9F16Ri4tS1sfD4+jA==",
"license": "MIT"
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
......@@ -1998,6 +2020,12 @@
"dev": true,
"license": "ISC"
},
"node_modules/hyparquet": {
"version": "1.25.1",
"resolved": "https://registry.npmjs.org/hyparquet/-/hyparquet-1.25.1.tgz",
"integrity": "sha512-CXcN/u6RdQqsK8IphUptpAEqY8IzgwzHY+MuXX+2wpoWTumfxPVr6JYbbywsNsiAl9aEbM5sRtxkwRBa22b49w==",
"license": "MIT"
},
"node_modules/iceberg-js": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz",
......@@ -2028,6 +2056,15 @@
"node": ">=12"
}
},
"node_modules/interval-tree-1d": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/interval-tree-1d/-/interval-tree-1d-1.0.4.tgz",
"integrity": "sha512-wY8QJH+6wNI0uh4pDQzMvl+478Qh7Rl4qLmqiluxALlNvl+I+o5x38Pw3/z7mDPTPS1dQalZJXsmbvxx5gclhQ==",
"license": "MIT",
"dependencies": {
"binary-search-bounds": "^2.0.0"
}
},
"node_modules/is-reference": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.3.tgz",
......@@ -2038,6 +2075,12 @@
"@types/estree": "^1.0.6"
}
},
"node_modules/isoformat": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/isoformat/-/isoformat-0.2.1.tgz",
"integrity": "sha512-tFLRAygk9NqrRPhJSnNGh7g7oaVWDwR0wKh/GM2LgmPa50Eg4UfyaCO4I8k6EqJHl1/uh2RAD6g06n5ygEnrjQ==",
"license": "ISC"
},
"node_modules/jiti": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
......
......@@ -19,9 +19,11 @@
"vite": "^7.3.1"
},
"dependencies": {
"@observablehq/plot": "^0.6.17",
"@supabase/supabase-js": "^2.98.0",
"d3": "^7.9.0",
"fuse.js": "^7.1.0",
"hyparquet": "^1.25.1",
"marked": "^17.0.4"
}
}
......
......@@ -32,8 +32,14 @@
});
</script>
<!-- MÓVIL: Búsqueda + Theme + hamburguesa arriba a la derecha -->
<!-- MÓVIL: Home + Búsqueda + Theme + hamburguesa arriba a la derecha -->
<div class="navbar-mobile">
<a href="/" class="nav-home-link" aria-label="Ir al inicio">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
<polyline points="9 22 9 12 15 12 15 22"/>
</svg>
</a>
<button
onclick={onOpenSearch}
class="nav-btn-icon-mobile"
......@@ -70,10 +76,25 @@
</button>
</div>
<!-- DESKTOP: Barra completa con Search + Theme + Menu -->
<!-- DESKTOP: Barra completa con Home + Search + Theme + Menu -->
<div class="navbar-desktop">
<div class="navbar-desktop-inner">
<!-- BOTÓN HOME -->
<div class="relative group/home">
<a href="/" class="nav-btn-icon" aria-label="Ir al inicio">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
<polyline points="9 22 9 12 15 12 15 22"/>
</svg>
</a>
<!-- Tooltip -->
<div class="absolute right-0 top-full mt-2 px-3 py-1.5 bg-gray-900/90 text-white text-xs rounded-lg whitespace-nowrap opacity-0 invisible group-hover/home:opacity-100 group-hover/home:visible transition-all duration-200 pointer-events-none backdrop-blur-sm">
Inicio
<div class="absolute right-3 bottom-full mb-[-4px] w-2 h-2 bg-gray-900/90 rotate-45"></div>
</div>
</div>
<!-- BOTÓN BÚSQUEDA GLOBAL -->
<div class="relative group/search">
<button
......@@ -152,6 +173,13 @@
z-index: 110;
align-items: center;
gap: 0.25rem;
padding: 6px;
background: rgba(40, 40, 38, 0.7);
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 14px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
}
.navbar-desktop {
......@@ -160,6 +188,20 @@
top: 1.5rem;
right: 1.5rem;
z-index: 110;
padding: 6px;
background: rgba(40, 40, 38, 0.7);
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 14px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);
}
:global(html:not(.dark)) .navbar-mobile,
:global(html:not(.dark)) .navbar-desktop {
background: rgba(255, 255, 255, 0.7);
border-color: rgba(0, 0, 0, 0.08);
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
}
.navbar-desktop-inner {
......@@ -201,16 +243,16 @@
align-items: center;
gap: 10px;
padding: 7px 12px 7px 12px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.12);
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
color: #9B9890;
color: #B8B5AD;
cursor: pointer;
transition: all 0.2s ease;
}
.nav-search-btn:hover {
background: rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.15);
border-color: rgba(201, 167, 81, 0.4);
color: #F5F0E8;
}
......@@ -252,28 +294,28 @@
align-items: center;
justify-content: center;
padding: 8px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.12);
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
color: #9B9890;
color: #B8B5AD;
cursor: pointer;
transition: all 0.2s ease;
}
.nav-btn-icon:hover {
background: rgba(255, 255, 255, 0.1);
background: rgba(255, 255, 255, 0.15);
border-color: rgba(201, 167, 81, 0.4);
color: #F5F0E8;
}
:global(html:not(.dark)) .nav-btn-icon {
background: rgba(0, 0, 0, 0.04);
border-color: rgba(0, 0, 0, 0.12);
color: #666;
background: rgba(0, 0, 0, 0.05);
border-color: rgba(0, 0, 0, 0.08);
color: #555;
}
:global(html:not(.dark)) .nav-btn-icon:hover {
background: rgba(0, 0, 0, 0.08);
background: rgba(0, 0, 0, 0.1);
border-color: rgba(201, 167, 81, 0.5);
color: #1A1A18;
}
......@@ -285,23 +327,66 @@
box-shadow: 0 1px 2px rgba(0,0,0,0.1), inset 0 1px 0 rgba(255,255,255,0.8);
}
/* Home link - hereda estilos de nav-btn-icon */
.nav-home-link {
display: flex;
align-items: center;
justify-content: center;
padding: 8px;
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 10px;
color: #B8B5AD;
cursor: pointer;
transition: all 0.2s ease;
text-decoration: none;
}
.nav-home-link:hover {
background: rgba(255, 255, 255, 0.15);
border-color: rgba(201, 167, 81, 0.4);
color: #F5F0E8;
}
:global(html:not(.dark)) .nav-home-link {
background: rgba(0, 0, 0, 0.05);
border-color: rgba(0, 0, 0, 0.08);
color: #555;
}
:global(html:not(.dark)) .nav-home-link:hover {
background: rgba(0, 0, 0, 0.1);
border-color: rgba(201, 167, 81, 0.5);
color: #1A1A18;
}
/* Mobile icon buttons */
.nav-btn-icon-mobile {
display: flex;
align-items: center;
justify-content: center;
padding: 8px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.12);
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 8px;
color: #9B9890;
color: #B8B5AD;
cursor: pointer;
transition: all 0.2s ease;
}
.nav-btn-icon-mobile:hover {
background: rgba(255, 255, 255, 0.18);
color: #F5F0E8;
}
:global(html:not(.dark)) .nav-btn-icon-mobile {
background: rgba(0, 0, 0, 0.04);
border-color: rgba(0, 0, 0, 0.12);
color: #666;
background: rgba(0, 0, 0, 0.06);
border-color: rgba(0, 0, 0, 0.06);
color: #555;
}
:global(html:not(.dark)) .nav-btn-icon-mobile:hover {
background: rgba(0, 0, 0, 0.12);
color: #1A1A18;
}
</style>
......
This diff is collapsed. Click to expand it.
......@@ -61,6 +61,43 @@ export async function initSearchIndex() {
throw errorRubros;
}
// Cargar finalidad y función
const { data: finfun, error: errorFinfun } = await supabase
.schema('ppto')
.from('clas_finfun')
.select('finfun, desc_finfun, nivel');
if (errorFinfun) {
console.error('Error cargando finalidad/función:', errorFinfun);
throw errorFinfun;
}
// Cargar organismos (no bloquea si falla)
let organismos = [];
const { data: organismosData, error: errorOrganismos } = await supabase
.schema('ppto')
.from('clas_organismos')
.select('organismo, organismo_desc_organismo, organismo_grupo, organismo_sub_grupo');
if (errorOrganismos) {
console.warn('Tabla clas_organismos no disponible:', errorOrganismos.message);
} else {
organismos = organismosData || [];
}
// Cargar fuentes (no bloquea si falla)
let fuentes = [];
const { data: fuentesData, error: errorFuentes } = await supabase
.schema('ppto')
.from('clas_fuentes')
.select('fuente, desc_fuente, grupo_fuente, desc_grupo_fuente');
if (errorFuentes) {
console.warn('Tabla clas_fuentes no disponible:', errorFuentes.message);
} else {
fuentes = fuentesData || [];
}
// Transformar entidades para el índice
const entidadesIndex = entidades.map(item => ({
tipo: 'entidad',
......@@ -102,8 +139,49 @@ export async function initSearchIndex() {
codigo_normalizado: item.rubro?.toString() || ''
}));
// Transformar finalidad y función para el índice
const nivelLabelsFinfun = { finalidad: 'Finalidad', grpfuncion: 'Grupo Función', funcion: 'Función' };
const finfunIndex = finfun.map(item => ({
tipo: 'finfun',
codigo: item.finfun,
nombre: item.desc_finfun,
sigla: null,
contexto: `Finalidad y Función · ${nivelLabelsFinfun[item.nivel] || item.nivel}`,
nivel: item.nivel,
nombre_normalizado: normalizeText(item.desc_finfun),
sigla_normalizada: '',
codigo_normalizado: item.finfun?.toString() || ''
}));
// Transformar organismos para el índice
// Todos los registros en la tabla son organismos (nivel hoja)
const organismosIndex = organismos.map(item => ({
tipo: 'organismo',
codigo: item.organismo,
nombre: item.organismo_desc_organismo,
sigla: null,
contexto: `Organismo · Grupo ${item.organismo_grupo}`,
nivel: 'organismo',
nombre_normalizado: normalizeText(item.organismo_desc_organismo),
sigla_normalizada: '',
codigo_normalizado: item.organismo?.toString() || ''
}));
// Transformar fuentes para el índice
const fuentesIndex = fuentes.map(item => ({
tipo: 'fuente',
codigo: item.fuente,
nombre: item.desc_fuente,
sigla: null,
contexto: `Fuente · ${item.desc_grupo_fuente}`,
nivel: 'fuente',
nombre_normalizado: normalizeText(item.desc_fuente),
sigla_normalizada: '',
codigo_normalizado: item.fuente?.toString() || ''
}));
// Combinar todos los conjuntos
searchData = [...entidadesIndex, ...objetosIndex, ...rubrosIndex];
searchData = [...entidadesIndex, ...objetosIndex, ...rubrosIndex, ...finfunIndex, ...organismosIndex, ...fuentesIndex];
fuseInstance = new Fuse(searchData, fuseOptions);
......
......@@ -8,7 +8,7 @@ export const isLoading = writable(false);
export const indexLoaded = writable(false);
export const error = writable(null);
export const selectedIndex = writable(-1);
export const filterType = writable('todos'); // todos, entidad, objeto_gasto, rubro
export const filterType = writable('todos'); // todos, entidad, objeto_gasto, rubro, finfun
// Estado derivado: si está mostrando resultados
export const showResults = derived(
......
This diff is collapsed. Click to expand it.
<script>
me pare<script>
import { supabase } from '$lib/supabase';
import { onMount, untrack } from 'svelte';
import { page } from '$app/stores';
......@@ -79,17 +79,24 @@
'10': 'Protección Social',
};
// Función para obtener color según la finalidad (primer dígito del código finfun)
// Helper para obtener la finalidad de un código (maneja caso especial de "10")
function getFinalidadKey(finfun) {
const code = String(finfun);
if (code.startsWith('10')) return '10';
return code.charAt(0);
}
// Función para obtener color según la finalidad
function getNodeColor(finfun, opacity = 1) {
const finalidadDigit = String(finfun).charAt(0);
const baseColor = FINALIDAD_COLORS[finalidadDigit] || '#888888';
const finalidadKey = getFinalidadKey(finfun);
const baseColor = FINALIDAD_COLORS[finalidadKey] || '#888888';
if (opacity === 1) return baseColor;
return d3.color(baseColor).copy({opacity}).formatRgb();
}
function getTextColor(finfun) {
const finalidadDigit = String(finfun).charAt(0);
const baseColor = FINALIDAD_COLORS[finalidadDigit] || '#888888';
const finalidadKey = getFinalidadKey(finfun);
const baseColor = FINALIDAD_COLORS[finalidadKey] || '#888888';
const color = d3.color(baseColor);
const r = color.r / 255;
const g = color.g / 255;
......@@ -99,8 +106,8 @@
}
function getTextColorSecondary(finfun) {
const finalidadDigit = String(finfun).charAt(0);
const baseColor = FINALIDAD_COLORS[finalidadDigit] || '#888888';
const finalidadKey = getFinalidadKey(finfun);
const baseColor = FINALIDAD_COLORS[finalidadKey] || '#888888';
const color = d3.color(baseColor);
const r = color.r / 255;
const g = color.g / 255;
......@@ -610,13 +617,26 @@
});
// Helpers para derivar jerarquía desde código finfun
// Jerarquía: Finalidad (1 dígito) → Grupo Función (2 dígitos) → Función (3+ dígitos)
// Jerarquía: Finalidad (1-9 o 10) → Grupo Función (2-3 dígitos) → Función (3+ dígitos)
// Nota: Finalidad 10 es especial - tiene 2 dígitos, sus hijos empiezan con "10"
function getFinalidadFromFinfun(finfun) {
return finfun.charAt(0);
const code = String(finfun);
// Si empieza con "10", la finalidad es "10"
if (code.startsWith('10')) {
return '10';
}
// Si no, la finalidad es el primer dígito
return code.charAt(0);
}
function getGrpFuncionFromFinfun(finfun) {
return finfun.substring(0, 2);
const code = String(finfun);
// Si empieza con "10", el grupo función son los primeros 3 dígitos (ej: "101", "102")
if (code.startsWith('10')) {
return code.substring(0, 3);
}
// Si no, son los primeros 2 dígitos (ej: "11", "21", "93")
return code.substring(0, 2);
}
function getItemsForFinalidad(finalidadCode) {
......
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
......@@ -48,16 +48,20 @@
});
// Helpers para derivar jerarquía desde código rubro
// Jerarquía: Tipo (XX000) → Clase (XXX00) → Cuenta (XXXX0) → Subcuenta (XXXXX)
function getTipoFromRubro(rubro) {
return rubro.charAt(0);
// Tipo = primeros 2 dígitos (XX de XX000)
return rubro.substring(0, 2);
}
function getClaseFromRubro(rubro) {
return rubro.substring(0, 2);
// Clase = primeros 3 dígitos (XXX de XXX00)
return rubro.substring(0, 3);
}
function getCuentaFromRubro(rubro) {
return rubro.substring(0, 3);
// Cuenta = primeros 4 dígitos (XXXX de XXXX0)
return rubro.substring(0, 4);
}
function getItemsForTipo(tipoCode) {
......
......@@ -2,18 +2,40 @@ import { supabase } from '$lib/supabase';
import { error } from '@sveltejs/kit';
// Funciones para derivar jerarquía del código finfun
// Jerarquía: Finalidad (1 dígito) → Grupo Función (2 dígitos) → Función (3+ dígitos)
// Jerarquía: Finalidad (1-9 o 10) → Grupo Función (2-3 dígitos) → Función (3+ dígitos)
// Nota: Finalidad 10 es especial - tiene 2 dígitos, sus hijos empiezan con "10"
function getFinalidadCode(finfun) {
return finfun.charAt(0);
const code = String(finfun);
// Si empieza con "10", la finalidad es "10"
if (code.startsWith('10')) {
return '10';
}
// Si no, la finalidad es el primer dígito
return code.charAt(0);
}
function getGrpFuncionCode(finfun) {
return finfun.substring(0, 2);
const code = String(finfun);
// Si empieza con "10", el grupo función son los primeros 3 dígitos (ej: "101", "102")
if (code.startsWith('10')) {
return code.substring(0, 3);
}
// Si no, son los primeros 2 dígitos (ej: "11", "21", "93")
return code.substring(0, 2);
}
function getNivel(finfun) {
if (finfun.length === 1) return 'finalidad';
if (finfun.length === 2) return 'grpfuncion';
const code = String(finfun);
// Finalidades 1-9 tienen longitud 1, finalidad 10 tiene longitud 2
if (code.length === 1) return 'finalidad';
if (code === '10') return 'finalidad';
// GrpFuncion: 2 dígitos para 1-9, 3 dígitos para 10
if (code.startsWith('10')) {
if (code.length === 3) return 'grpfuncion';
return 'funcion';
}
if (code.length === 2) return 'grpfuncion';
return 'funcion';
}
......@@ -77,27 +99,40 @@ export async function load({ params }) {
// Buscar hijos según nivel
if (nivel === 'finalidad') {
// Hijos: grupos de función que empiecen con el mismo dígito
// Hijos: grupos de función de esta finalidad
const { data: hijosData } = await supabase
.schema('ppto')
.from('clas_finfun')
.select('*')
.eq('nivel', 'grpfuncion')
.like('finfun', `${codigo}%`)
.order('finfun');
hijos = hijosData || [];
// Filtrar solo los hijos directos de esta finalidad
// Para finalidad "1": hijos son "11", "12", ..., "19" (NO "10x")
// Para finalidad "10": hijos son "101", "102", ..., "109"
if (hijosData) {
hijos = hijosData.filter(h => {
const hijoPadre = getFinalidadCode(h.finfun);
return hijoPadre === codigo;
});
}
} else if (nivel === 'grpfuncion') {
// Hijos: funciones que empiecen con los mismos 2 dígitos
// Hijos: funciones de este grupo
const grpCode = getGrpFuncionCode(codigo);
const { data: hijosData } = await supabase
.schema('ppto')
.from('clas_finfun')
.select('*')
.eq('nivel', 'funcion')
.like('finfun', `${codigo}%`)
.order('finfun');
hijos = hijosData || [];
// Filtrar solo los hijos directos de este grupo
if (hijosData) {
hijos = hijosData.filter(h => {
const hijoGrp = getGrpFuncionCode(h.finfun);
return hijoGrp === grpCode;
});
}
}
// funcion no tiene hijos
......
......@@ -1029,9 +1029,9 @@
<p class="context-hint">Funciones relacionadas. <span class="desktop-hint">Haz click</span><span class="mobile-hint">Toca</span> para explorar.</p>
<div class="tree">
{#each padres as padre, i}
<a href="/finfun/{padre.objeto}" class="tree-node has-tooltip" style="--indent: {i}" data-tooltip="{padre.desc_finfun}">
<a href="/finfun/{padre.finfun}" class="tree-node has-tooltip" style="--indent: {i}" data-tooltip="{padre.desc_finfun}">
<span class="tree-dot"></span>
<span class="tree-code">{padre.objeto}</span>
<span class="tree-code">{padre.finfun}</span>
<span class="tree-name">{padre.desc_finfun}</span>
</a>
{/each}
......@@ -1045,9 +1045,9 @@
{#if hijos.length > 0}
<div class="tree-children" style="--indent: {padres.length + 1}">
{#each hijos as hijo}
<a href="/finfun/{hijo.objeto}" class="tree-node tree-node-child has-tooltip" data-tooltip="{hijo.desc_finfun}">
<a href="/finfun/{hijo.finfun}" class="tree-node tree-node-child has-tooltip" data-tooltip="{hijo.desc_finfun}">
<span class="tree-dot"></span>
<span class="tree-code">{hijo.objeto}</span>
<span class="tree-code">{hijo.finfun}</span>
<span class="tree-name">{hijo.desc_finfun}</span>
</a>
{/each}
......
import { supabase } from '$lib/supabase';
import { error } from '@sveltejs/kit';
export async function load({ params }) {
console.time('[SERVER] Total load fuente');
const { codigo } = params;
console.time('[SERVER] Query clas_fuentes');
const { data, error: dbError } = await supabase
.schema('ppto')
.from('clas_fuentes')
.select('*')
.eq('fuente', codigo);
console.timeEnd('[SERVER] Query clas_fuentes');
if (dbError || !data || data.length === 0) {
throw error(404, 'Fuente no encontrada');
}
const fuente = data[0];
// Obtener hermanos (otras fuentes del mismo grupo)
let hermanos = [];
console.time('[SERVER] Query hermanos');
const { data: hermanosData } = await supabase
.schema('ppto')
.from('clas_fuentes')
.select('*')
.eq('grupo_fuente', fuente.grupo_fuente)
.neq('fuente', codigo)
.order('fuente');
if (hermanosData) {
hermanos = hermanosData;
}
console.timeEnd('[SERVER] Query hermanos');
console.timeEnd('[SERVER] Total load fuente');
return {
fuente,
hermanos
};
}
This diff is collapsed. Click to expand it.
import { supabase } from '$lib/supabase';
import { error } from '@sveltejs/kit';
export async function load({ params }) {
console.time('[SERVER] Total load organismo');
const { codigo } = params;
console.time('[SERVER] Query clas_organismos');
const { data, error: dbError } = await supabase
.schema('ppto')
.from('clas_organismos')
.select('*')
.eq('organismo', codigo);
console.timeEnd('[SERVER] Query clas_organismos');
if (dbError || !data || data.length === 0) {
throw error(404, 'Organismo no encontrado');
}
const organismo = data[0];
// Obtener hermanos (otros organismos del mismo subgrupo)
let hermanos = [];
console.time('[SERVER] Query hermanos');
const { data: hermanosData } = await supabase
.schema('ppto')
.from('clas_organismos')
.select('*')
.eq('organismo_grupo', organismo.organismo_grupo)
.eq('organismo_sub_grupo', organismo.organismo_sub_grupo)
.neq('organismo', codigo)
.order('organismo');
if (hermanosData) {
hermanos = hermanosData;
}
console.timeEnd('[SERVER] Query hermanos');
console.timeEnd('[SERVER] Total load organismo');
return {
organismo,
hermanos
};
}
This diff is collapsed. Click to expand it.
import { supabase } from '$lib/supabase';
import { error } from '@sveltejs/kit';
// Funciones para derivar jerarquía del código rubro
// Jerarquía: Tipo (XX000) → Clase (XXX00) → Cuenta (XXXX0) → Subcuenta (XXXXX)
function getTipoCode(rubro) {
// Primeros 2 dígitos + 000 → ej: 11000, 12000
return rubro.substring(0, 2) + '000';
}
function getClaseCode(rubro) {
// Primeros 3 dígitos + 00 → ej: 11100, 11200
return rubro.substring(0, 3) + '00';
}
function getCuentaCode(rubro) {
// Primeros 4 dígitos + 0 → ej: 11110, 11120
return rubro.substring(0, 4) + '0';
}
function getNivelFromCode(rubro) {
// Determinar nivel basado en el patrón de ceros
if (rubro.endsWith('000')) return 'tipo'; // XX000
if (rubro.endsWith('00')) return 'clase'; // XXX00
if (rubro.endsWith('0')) return 'cuenta'; // XXXX0
return 'sub_cuenta'; // XXXXX
}
function getNextNivel(nivel) {
const niveles = { tipo: 'clase', clase: 'cuenta', cuenta: 'sub_cuenta' };
return niveles[nivel] || null;
}
export async function load({ params }) {
const { codigo } = params;
......@@ -15,88 +46,81 @@ export async function load({ params }) {
}
const rubro = data[0];
const nivel = rubro.nivel || getNivelFromCode(codigo);
// Obtener jerarquía (padres e hijos)
let padres = [];
let hijos = [];
// Buscar padre según nivel
if (rubro.nivel === 'sub_cuenta') {
// Padre es cuenta
const { data: padreData } = await supabase
// Buscar padres según nivel
if (nivel === 'sub_cuenta') {
// Padres: tipo, clase, cuenta
const tipoCode = getTipoCode(codigo);
const claseCode = getClaseCode(codigo);
const cuentaCode = getCuentaCode(codigo);
const { data: padresData } = await supabase
.schema('ppto')
.from('clas_rubros')
.select('*')
.eq('nivel', 'cuenta')
.eq('tipo', rubro.tipo)
.eq('clase', rubro.clase)
.eq('cuenta', rubro.cuenta);
if (padreData?.length) padres.push(padreData[0]);
}
.in('rubro', [tipoCode, claseCode, cuentaCode])
.order('rubro');
if (padresData) padres = padresData;
} else if (nivel === 'cuenta') {
// Padres: tipo, clase
const tipoCode = getTipoCode(codigo);
const claseCode = getClaseCode(codigo);
if (rubro.nivel === 'cuenta' || rubro.nivel === 'sub_cuenta') {
// Padre es clase
const { data: padreData } = await supabase
const { data: padresData } = await supabase
.schema('ppto')
.from('clas_rubros')
.select('*')
.eq('nivel', 'clase')
.eq('tipo', rubro.tipo)
.eq('clase', rubro.clase);
if (padreData?.length) padres.unshift(padreData[0]);
}
.in('rubro', [tipoCode, claseCode])
.order('rubro');
if (rubro.nivel !== 'tipo') {
// Padre es tipo
const { data: padreData } = await supabase
if (padresData) padres = padresData;
} else if (nivel === 'clase') {
// Padre: tipo
const tipoCode = getTipoCode(codigo);
const { data: padresData } = await supabase
.schema('ppto')
.from('clas_rubros')
.select('*')
.eq('nivel', 'tipo')
.eq('tipo', rubro.tipo);
if (padreData?.length) padres.unshift(padreData[0]);
.eq('rubro', tipoCode);
if (padresData) padres = padresData;
}
// tipo no tiene padres
// Buscar hijos según nivel
if (rubro.nivel === 'tipo') {
const nextNivel = getNextNivel(nivel);
if (nextNivel) {
const { data: hijosData } = await supabase
.schema('ppto')
.from('clas_rubros')
.select('*')
.eq('nivel', 'clase')
.eq('tipo', rubro.tipo)
.eq('nivel', nextNivel)
.like('rubro', `${codigo.replace(/0+$/, '')}%`)
.order('rubro');
hijos = hijosData?.reduce((acc, item) => {
if (!acc.find(h => h.rubro === item.rubro)) acc.push(item);
return acc;
}, []) || [];
} else if (rubro.nivel === 'clase') {
const { data: hijosData } = await supabase
.schema('ppto')
.from('clas_rubros')
.select('*')
.eq('nivel', 'cuenta')
.eq('tipo', rubro.tipo)
.eq('clase', rubro.clase)
.order('rubro');
hijos = hijosData?.reduce((acc, item) => {
if (!acc.find(h => h.rubro === item.rubro)) acc.push(item);
return acc;
}, []) || [];
} else if (rubro.nivel === 'cuenta') {
const { data: hijosData } = await supabase
.schema('ppto')
.from('clas_rubros')
.select('*')
.eq('nivel', 'sub_cuenta')
.eq('tipo', rubro.tipo)
.eq('clase', rubro.clase)
.eq('cuenta', rubro.cuenta)
.order('rubro');
hijos = hijosData?.reduce((acc, item) => {
if (!acc.find(h => h.rubro === item.rubro)) acc.push(item);
return acc;
}, []) || [];
if (hijosData) {
// Filtrar solo hijos directos (siguiente nivel)
hijos = hijosData.filter(h => {
if (nivel === 'tipo') {
// Hijos de tipo son clase: XXX00 donde XX = tipo
return h.rubro.startsWith(codigo.substring(0, 2)) && h.nivel === 'clase';
} else if (nivel === 'clase') {
// Hijos de clase son cuenta: XXXX0 donde XXX = clase
return h.rubro.startsWith(codigo.substring(0, 3)) && h.nivel === 'cuenta';
} else if (nivel === 'cuenta') {
// Hijos de cuenta son sub_cuenta: XXXXX donde XXXX = cuenta
return h.rubro.startsWith(codigo.substring(0, 4)) && h.nivel === 'sub_cuenta';
}
return false;
});
}
}
return {
......
// Desactivar SSR para esta página porque hyparquet y @observablehq/plot
// requieren APIs del navegador (fetch con ArrayBuffer, DOM)
export const ssr = false;
This diff is collapsed. Click to expand it.
No preview for this file type
No preview for this file type
No preview for this file type
No preview for this file type