Plataforma técnica · Sevastopol
Activos Fijos Island
Activo Fijo Islands Sevastopol
Propósito
Section titled “Propósito”ActivosFijosIsland es la island SolidJS que opera todo el ciclo de activo fijo desde Sevastopol: reconocimiento inicial, baja, mantenimiento de categorías y depreciación periódica. Es un único componente con tres pestañas; toda la persistencia y la validación ocurren en Orchestrator y la island actúa como interfaz operativa y consumidora del proxy /api/activo-fijo.
Ubicación
Section titled “Ubicación”Directorysevastopol/src/components/islands/contabilidad/
- ActivosFijosIsland.tsx — componente único, 3 pestañas
Directorysevastopol/src/pages/api/activo-fijo/
- index.ts
- […path].ts — proxy genérico a Orchestrator
Directorysevastopol/src/lib/hooks/
- useActiveTenant.ts
- useActivePeriod.ts
Composición
Section titled “Composición”| Pestaña | Función | Endpoint base |
|---|---|---|
activos | Consulta, alta y baja de activos. | /api/activo-fijo/activos |
categorias | Alta y edición de categorías tributarias, vida útil y cuentas. | /api/activo-fijo/categorias |
depreciacion | Consulta, generación, contabilización, reversa y eliminación de cuotas. | /api/activo-fijo/depreciacion |
La pestaña activa vive en una signal local; las tres dependen del tenant activo y la pestaña de depreciación además del periodo activo global.
const [tab, setTab] = createSignal<'activos' | 'categorias' | 'depreciacion'>('activos');const AF_API = '/api/activo-fijo';Hooks y Estado Global
Section titled “Hooks y Estado Global”La island consume dos hooks compartidos del repositorio Sevastopol. Ambos disparan recargas reactivas cuando su valor cambia.
import { useActiveTenant } from '@/lib/hooks/useActiveTenant';import { useActivePeriod } from '@/lib/hooks/useActivePeriod';
const tenantId = useActiveTenant();const { period } = useActivePeriod(() => { if (tab() !== 'depreciacion') return; const tid = tenantId(); if (!tid) return; void loadDepreciacion(tid);});| Hook | Aporta | Reactividad |
|---|---|---|
useActiveTenant | UUID de la base tenant seleccionada en TenantSelectorBar. | Cambia al cambiar tenant. |
useActivePeriod | { year, month } global del contexto contable. | Cambia al cambiar periodo; el callback dispara la recarga sólo cuando la pestaña activa es depreciacion. |
El estado de fila (activos, categorias, depRows), formularios (crearActivoForm, catForm, generarForm) y banderas de loading son todas signals locales (createSignal), sin store global propio.
Pestaña Activos
Section titled “Pestaña Activos”Alta de Activo
Section titled “Alta de Activo”-
Antes de mostrar el modal, precargar compras disponibles, tabla de vida útil y categorías.
await loadCompras(tid);await loadVidaUtil();await loadCategorias(tid);resetCrearForm();setShowCrearActivo(true); -
Validar campos obligatorios antes de enviar.
const f = crearActivoForm();if (!f.categoria_id || !f.nombre || !f.monto_neto || !f.fecha_compra) {toast.push('Completa los campos obligatorios', 'error');return;} -
Construir el cuerpo; la fecha de inicio de depreciación se iguala a la fecha de compra.
const body: Record<string, any> = {categoria_id: Number(f.categoria_id),nombre: f.nombre.trim(),rut_proveedor: f.rut_proveedor.trim(),monto_neto: Number(f.monto_neto),fecha_compra: f.fecha_compra,fecha_inicio_dep: f.fecha_compra,}; -
Si hay compra de origen, enriquecer el cuerpo con datos de la línea.
if (f.compra_id) {const compra = compras().find((c) => c.id === f.compra_id);if (compra) {body.compra_id = compra.id;body.rut_proveedor = compra.rut_emisor ?? body.rut_proveedor;body.compra_año = compra.año;body.compra_mes = compra.mes;if (compra.folio) body.folio_factura = String(compra.folio);}} -
Enviar al proxy local y recargar la lista.
const r = await authenticatedFetch(`${AF_API}/activos?tenant_id=${tid}`, {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify(body),});await handleAuthResponse(r);if (r.ok) {toast.push('Activo creado correctamente', 'success');setShowCrearActivo(false);await loadActivos(tid);}
Baja de Activo
Section titled “Baja de Activo”Solo aplica sobre activos en estado ACTIVO. El modal exige fecha y motivo opcional.
const r = await authenticatedFetch(`${AF_API}/activos/${id}/baja?tenant_id=${tid}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ estado: 'DADO_DE_BAJA', fecha_baja, motivo_baja }),});Columnas Visibles
Section titled “Columnas Visibles”| Columna | Origen |
|---|---|
| Activo | nombre |
| Categoría | nombre_categoria |
| Compra | fecha_compra |
| Monto Neto | monto_neto |
| Valor Libro | valor_libro_actual |
| Depreciado | pct_depreciado |
| Estado | estado |
Pestaña Categorías
Section titled “Pestaña Categorías”La categoría es el contrato contable del activo. Se crea antes que cualquier activo y fija las tres cuentas contables y la vida útil normal/acelerada.
-
Validar todos los campos, incluida la selección SII.
if (!f.nombre || !f.seccion_sii || !f.numero_item_sii ||!f.vida_util_anos || !f.dep_acelerada_anos ||!f.cuenta_activo_codigo || !f.cuenta_dep_acumulada_codigo || !f.cuenta_gasto_dep_codigo) {toast.push('Completa todos los campos obligatorios (incluye selección SII)', 'error');return;} -
Enviar la categoría al proxy local y recargar.
const r = await authenticatedFetch(`${AF_API}/categorias?tenant_id=${tid}`, {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify({nombre: f.nombre.trim(),seccion_sii: f.seccion_sii,vida_util_anos: Number(f.vida_util_anos),dep_acelerada_anos: Number(f.dep_acelerada_anos),cuenta_activo_codigo: f.cuenta_activo_codigo.trim(),cuenta_dep_acumulada_codigo: f.cuenta_dep_acumulada_codigo.trim(),cuenta_gasto_dep_codigo: f.cuenta_gasto_dep_codigo.trim(),}),});
Las cuentas contables se eligen desde el plan contable del tenant, filtradas por cuentas padre fijas: 1201000 para activo y 1202000 para depreciación acumulada.
Pestaña Depreciación
Section titled “Pestaña Depreciación”Consulta del Periodo
Section titled “Consulta del Periodo”El periodo se arma desde useActivePeriod y se consulta sólo cuando la pestaña está activa.
function activeDepPeriodoMes() { const now = new Date(); const ano = String(period().year ?? now.getFullYear()); const mes = String(period().month ?? (now.getMonth() + 1)).padStart(2, '0'); return `${ano}-${mes}-01`;}
async function loadDepreciacion(tid: string) { const periodoMes = activeDepPeriodoMes(); const r = await authenticatedFetch(`${AF_API}/depreciacion/periodo/${periodoMes}?tenant_id=${tid}`); await handleAuthResponse(r); setDepRows(r.ok ? await r.json() : []);}Generación por Categoría
Section titled “Generación por Categoría”La UI envía categoría, periodo y tipo de depreciación; Orchestrator procesa cada activo elegible de esa categoría y devuelve { procesados, omitidos, errores }.
const r = await authenticatedFetch(`${AF_API}/depreciacion/generar-categoria?tenant_id=${tid}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ categoria_id: Number(f.categoria_id), periodo_mes: f.periodo_mes, tipo_depreciacion: f.tipo_depreciacion, ...(f.notas ? { notas: f.notas } : {}), }),});
const { procesados, omitidos, errores } = await r.json();if (procesados > 0) toast.push(`${procesados} activo(s) depreciado(s)`, 'success');else if (omitidos > 0) toast.push(`${omitidos} ya tenían cuota para este período`, 'success');for (const err of errores as string[]) toast.push(err, 'error');Acciones por Estado
Section titled “Acciones por Estado”La fila de cada cuota expone acciones distintas según el estado, para evitar acciones inválidas desde la UI:
| Estado | Acción visible | Endpoint |
|---|---|---|
PENDIENTE | Contabilizar | PUT /depreciacion/:id/contabilizar |
PENDIENTE | Eliminar | DELETE /depreciacion/:id |
CONTABILIZADO | Reversar | PUT /depreciacion/:id/reversar-contabilizacion |
const ok = confirm( '¿Reversar contabilización de esta depreciación? Se eliminarán sus líneas de movimiento y quedará en PENDIENTE.',);if (!ok) return;Columnas Visibles
Section titled “Columnas Visibles”| Columna | Origen |
|---|---|
| Activo | nombre_activo |
| Periodo | periodo_mes |
| Tipo | tipo_depreciacion |
| Número | periodo_numero |
| Cuota | cuota_depreciacion |
| Depreciación acumulada | dep_acumulada_posterior |
| Valor libro | valor_libro |
| Estado | estado |
Endpoints Consumidos
Section titled “Endpoints Consumidos”| Método | Ruta | Operación |
|---|---|---|
GET | /api/activo-fijo/activos | Lista activos por estado. |
POST | /api/activo-fijo/activos | Da de alta un activo. |
PUT | /api/activo-fijo/activos/:id/baja | Da de baja un activo. |
GET | /api/activo-fijo/categorias | Lista categorías (todas=true incluye inactivas). |
POST | /api/activo-fijo/categorias | Crea una categoría. |
PUT | /api/activo-fijo/categorias/:id | Actualiza una categoría. |
GET | /api/activo-fijo/compras | Lista compras SII disponibles para alta. |
GET | /api/activo-fijo/depreciacion/periodo/:yyyy-mm-01 | Cuotas del periodo. |
POST | /api/activo-fijo/depreciacion/generar-categoria | Genera cuotas para una categoría. |
PUT | /api/activo-fijo/depreciacion/:id/contabilizar | Contabiliza una cuota pendiente. |
PUT | /api/activo-fijo/depreciacion/:id/reversar-contabilizacion | Reversa una cuota contabilizada. |
DELETE | /api/activo-fijo/depreciacion/:id | Elimina una cuota pendiente. |
Proxy Local
Section titled “Proxy Local”Sevastopol no llama directamente a Orchestrator. La ruta src/pages/api/activo-fijo/[...path].ts reenvía método, query string, cookies, Authorization y cuerpo hacia Orchestrator preservando credenciales.
let targetUrl = `${ORCHESTRATOR_BASE_URL}/api/activo-fijo/${restPath}`;if (url.search) targetUrl += url.search;
const orchestratorRes = await fetch(targetUrl, { method: request.method, headers: getProxyHeaders(request), body, credentials: 'include',});Reglas de UI
Section titled “Reglas de UI”| Regla | Motivo |
|---|---|
| Usar tenant activo | Activos, categorías y cuotas pertenecen a una base tenant. |
Usar periodo activo en depreciacion | La consulta de cuotas se acota al mes contable global. |
| Cargar compras al abrir el alta | El activo puede enlazarse a la línea de compra que lo originó. |
Igualar fecha_inicio_dep a fecha_compra | El flujo actual inicia la depreciación en la fecha de adquisición. |
| Crear la categoría antes que el activo | El activo exige una categoría existente que parametrice sus cuentas. |
| Generar depreciación por categoría | El flujo actual procesa activos elegibles de una categoría completa, no por activo individual. |
| Confirmar reversa explícitamente | La reversa elimina líneas contables y debe ser consciente. |
| Mostrar acciones por estado | Evita acciones inválidas desde la UI. |
| Recargar tras cada acción | Mantiene la tabla alineada con el estado real en Orchestrator. |