erp-core/docs/05-user-stories/mgn-004/US-MGN-004-001-002-jerarquia-cuentas-contables.md

8.9 KiB
Raw Blame History

US-MGN-004-001-002: Jerarquía y Vista Árbol de Cuentas Contables

RF Asociado: RF-MGN-004-001 Módulo: MGN-004 - Financiero Básico Epic: Plan de Cuentas Prioridad: P0 (MVP) Story Points: 3 Sprint: Sprint 7 Estado: Ready for Development Fecha: 2025-11-24


User Story

Como contador de la empresa, Quiero organizar las cuentas contables en una estructura jerárquica (árbol), Para agrupar las cuentas por categorías y subcategorías y visualizarlas de forma organizada.


Descripción Detallada

Esta user story implementa la funcionalidad de jerarquía de cuentas contables mediante el uso del campo parent_id. Permite:

  • Crear cuentas con cuentas padre (subcuentas)
  • Visualizar el plan de cuentas como árbol jerárquico
  • Calcular niveles de profundidad (nivel 0 = cuenta raíz, nivel 1 = hijos directos, etc.)
  • Navegar por la jerarquía (expandir/colapsar nodos)

Ejemplo de jerarquía:

1 - Activos (parent_id: null)
  1.1 - Activos Corrientes (parent_id: 1)
    1.1.01 - Efectivo y Equivalentes (parent_id: 1.1)
      1.1.01.001 - Caja General (parent_id: 1.1.01)
      1.1.01.002 - Banco Santander (parent_id: 1.1.01)

Criterios de Aceptación

Escenario 1: Crear cuenta con cuenta padre

Dado que tengo una cuenta padre "1.1 Activos Corrientes" con id=10, Cuando creo una cuenta "1.1.01 Efectivo" con parent_id=10, Entonces el sistema crea la cuenta y establece la relación jerárquica correctamente.

Escenario 2: Obtener árbol jerárquico de cuentas

Dado que tengo cuentas con jerarquía definida, Cuando solicito GET /api/v1/financial/accounts/tree, Entonces el sistema retorna un JSON con estructura de árbol incluyendo hijos (children array).

Escenario 3: Validar que parent_id existe

Dado que intento crear una cuenta con parent_id=999 (no existe), Cuando envío el request, Entonces el sistema retorna error 400 "Cuenta padre no encontrada".

Escenario 4: Prevenir ciclos en jerarquía

Dado que tengo cuenta A (id=1) con parent_id=null, Cuando intento actualizar cuenta A con parent_id=1 (a sí misma), Entonces el sistema retorna error 400 "Una cuenta no puede ser su propia cuenta padre".

Escenario 5: Calcular nivel jerárquico

Dado que tengo una cuenta con 3 niveles de padres, Cuando consulto el endpoint GET /api/v1/financial/accounts/:id?include=hierarchy_level, Entonces el sistema retorna hierarchy_level=3.


Reglas de Negocio

  • RN-1: Una cuenta puede tener parent_id (opcional). Si parent_id=null, es cuenta raíz.
  • RN-2: parent_id debe existir en la tabla accounts y pertenecer a la misma empresa.
  • RN-3: Una cuenta no puede ser su propia cuenta padre (prevenir ciclos directos).
  • RN-4: No se permiten ciclos indirectos (A → B → C → A). Validar recursivamente.
  • RN-5: Nivel jerárquico se calcula recursivamente: nivel_padre + 1. Máximo 10 niveles.
  • RN-6: Al eliminar (soft delete) una cuenta padre, las cuentas hijas NO se eliminan automáticamente.

Tareas Técnicas (Checklist de Implementación)

Backend

  • Endpoint: GET /api/v1/financial/accounts/tree (retorna estructura jerárquica)
  • Endpoint: GET /api/v1/financial/accounts/:id/children (hijos directos)
  • Endpoint: GET /api/v1/financial/accounts/:id/ancestors (padres hasta la raíz)
  • Service método: AccountService.getTree() (construye árbol con children)
  • Service método: AccountService.getChildren(parentId)
  • Service método: AccountService.getAncestors(accountId)
  • Service método: AccountService.validateParent(parentId) (existe y no crea ciclos)
  • Service método: AccountService.calculateLevel(accountId) (recursivo)
  • Validación: Prevenir ciclos directos e indirectos
  • Unit tests: Jerarquía (6 test cases)
  • Integration tests: Tree endpoints (5 test cases)
  • Swagger docs para endpoints de jerarquía

Frontend

  • Componente: AccountTree.tsx (vista árbol con expandir/colapsar)
  • Componente: AccountNode.tsx (nodo individual del árbol)
  • Componente: AccountHierarchyBreadcrumb.tsx (breadcrumb de jerarquía)
  • Hook: useAccountTree.ts (manejo de estado del árbol)
  • Función: buildTree(flatAccounts) (convierte array plano a árbol)
  • Icono: Expandir/Colapsar nodos (chevron-right / chevron-down)
  • Indentación visual por nivel (padding-left: nivel * 20px)
  • Component tests: AccountTree.test.tsx (5 test cases)
  • E2E test: "should expand and collapse account tree nodes"

Database

  • Validar foreign key: accounts.parent_id → accounts.id
  • Índice: idx_accounts_parent_id (ya existe)
  • Query recursivo: Common Table Expression (CTE) para calcular niveles
  • Seed data: Plan de cuentas jerárquico de ejemplo (3 niveles)

Mockups / Wireframes

Vista Árbol de Plan de Cuentas:

📂 1 - Activos                                [Editar] [+]
  📂 1.1 - Activos Corrientes                [Editar] [+]
    📂 1.1.01 - Efectivo y Equivalentes      [Editar] [+]
      📄 1.1.01.001 - Caja General           [Editar] [×]
      📄 1.1.01.002 - Banco Santander        [Editar] [×]
    📂 1.1.02 - Cuentas por Cobrar           [Editar] [+]
📂 2 - Pasivos                               [Editar] [+]
  📂 2.1 - Pasivos Corrientes                [Editar] [+]

Interacciones:

  • Click en icono 📂 / 📄 : Expandir/colapsar nodos
  • Click en [+]: Crear subcuenta
  • Click en nombre: Ver detalle de cuenta
  • Hover: Resaltar nodo completo

Casos de Prueba (Test Scenarios)

Pruebas Funcionales

  1. TC-001: Crear cuenta con parent_id válido

    • Input: parent_id=10 (existe)
    • Expected: Status 201, cuenta creada con parent_id=10
  2. TC-002: Error por parent_id inexistente

    • Input: parent_id=999
    • Expected: Status 400, error "Cuenta padre no encontrada"
  3. TC-003: Prevenir ciclo directo (A → A)

    • Input: id=1, parent_id=1
    • Expected: Status 400, error "Cuenta no puede ser su propia padre"
  4. TC-004: Prevenir ciclo indirecto (A → B → A)

    • Input: Cuenta B (parent_id=A) luego actualizar A (parent_id=B)
    • Expected: Status 400, error "Ciclo detectado en jerarquía"
  5. TC-005: Obtener árbol jerárquico

    • Input: GET /accounts/tree
    • Expected: JSON con structure { id, code, name, children: [...] }
  6. TC-006: Calcular nivel jerárquico correcto

    • Input: Cuenta con 3 niveles de padres
    • Expected: hierarchy_level=3

Pruebas No Funcionales

  1. Performance: GET /accounts/tree < 500ms (hasta 1000 cuentas)
  2. Usabilidad: Árbol carga niveles lazy (cargar hijos al expandir)

Dependencias

  • US bloqueantes:
    • US-MGN-004-001-001 (CRUD de Cuentas) - Requerido para crear cuentas base
  • Módulos requeridos: MGN-004

Notas de Implementación

  • Usar Common Table Expressions (CTE) recursivos para calcular niveles y ancestros:
WITH RECURSIVE account_hierarchy AS (
  SELECT id, parent_id, code, name, 0 as level
  FROM financial.accounts
  WHERE parent_id IS NULL
  UNION ALL
  SELECT a.id, a.parent_id, a.code, a.name, ah.level + 1
  FROM financial.accounts a
  JOIN account_hierarchy ah ON a.parent_id = ah.id
)
SELECT * FROM account_hierarchy ORDER BY code;
  • Validación de ciclos: Función recursiva que recorre padres hasta root o detecta ciclo
  • Frontend: Usar react-arborist o react-complex-tree para vista árbol avanzada
  • Cache: Cachear árbol completo en Redis (TTL 10 minutos) para evitar recalcular

Estimación Detallada

Tarea Estimación
Backend Development 2 horas
Frontend Development (Tree Component) 2 horas
Testing 1.5 horas
Code Review 0.5 hora
TOTAL 6 horas

Equivalente: 3 Story Points


Definition of Done (DoD)

  • Código backend implementado (tree endpoints + validación ciclos)
  • Código frontend implementado (AccountTree component)
  • Unit tests escritos y pasando (cobertura > 80%)
  • Integration tests pasando
  • E2E test pasando (expand/collapse tree)
  • Code review aprobado
  • Documentación Swagger actualizada
  • RLS aplicado
  • Merge a develop
  • QA manual completado
  • PO aprobado

Referencias