Skip to content

Plataforma técnica · Orchestrator

Manual de Cuentas (Admin)

Orchestrator Command Manual Cuentas

ManualCuentasService administra el catálogo canónico de documentación contable: para cada código de cuenta, qué se carga al debe, qué al haber, asientos tipo, normas NIC/IFRS aplicables, ejemplos y referencias cruzadas. A diferencia del plan de cuentas (estructura jerárquica, vive en PostgreSQL), el manual es prosa estructurada por cuenta y vive en MongoDB.

Cada documento de manual tiene un schema flexible: arrays de asientos tipo de longitud variable, prosa con markdown, tags, ver-tambien, metadata de versión. El acceso es siempre findByCodigo o $text search, no joins ni transacciones — un caso de uso clásico de documento.

ManualCuenta:

CampoTipoNotas
codigostringCódigo de cuenta (e.g. 1101030). Único.
es_grupobooleanDerivado del plan: la cuenta tiene hijos.
debestring (markdown)Qué se carga al debe.
haberstring (markdown)Qué se carga al haber.
nic_ifrs{ norma?, tratamiento?, referencia? }Norma aplicable (e.g. NIIF 15).
ejemplosArray<{ titulo, descripcion_md }>Casos prácticos.
asientos_tipoArray<{ titulo, debe[], haber[] }>Plantillas de asiento.
ver_tambienstring[]Códigos relacionados.
tagsstring[]Para búsqueda.
meta{ autor?, created_at, updated_at, version }Versioning incremental.

ManualCuentasRepository.ensureIndexes crea (una sola vez, memoizado):

ÍndiceTipoPara
{ codigo: 1 }UNIQUELookup por código (findByCodigo).
{ debe, haber, nic_ifrs.tratamiento, tags }TEXTBúsqueda full-text de cuenta.
flowchart LR
  R["routes/command/manual-cuentas/"]
  S["ManualCuentasService"]
  REP["ManualCuentasRepository<br/>(estático)"]
  M[("MongoDB · nostromo_manual<br/>collection: cuentas")]
  PL["lib/plan-cuentas-source.ts"]
  TPL[("PostgreSQL · accounting_template<br/>plan_contable")]

  R --> S
  S --> REP --> M
  S -. valida código existe .-> PL --> TPL

El servicio cruza dos motores de datos. Antes de un upsert, valida que el codigo exista en el plan template de PostgreSQL — no permite documentar cuentas inexistentes en el plan oficial. Y deriva es_grupo consultando si el código tiene hijos (planCuentaHasChildren).

MétodoValidaciónEfecto
getByCodigo(codigo)Lookup directo en Mongo.
list(opts)limit clamp a [1, 100], skip ≥ 0.Paginación + búsqueda $text opcional.
save(dto)codigo requerido y existente en accounting_template.plan_contable.Upsert + bumpea meta.version.
remove(codigo)deleteOne({ codigo }).

En cada save:

CampoComportamiento
meta.created_atPreservado si ya existía; nuevo now() si es creación.
meta.updated_atSiempre now().
meta.version(actual ?? 0) + 1. Incrementa monotónico, sin saltos.
meta.autorPreservado del documento previo (no se sobrescribe).

No hay histórico de versiones anteriores — replaceOne sobreescribe. Si se necesita auditoría granular, usar el audit log con tableName: 'manual_cuentas' desde el handler.

Los endpoints viven en routes/command/manual-cuentas/index.ts bajo /api/command/manual-cuentas y están detallados en Command Center API.

Resumen:

MétodoRutaUso
GET/api/command/manual-cuentasLista con paginación / search.
GET/api/command/manual-cuentas/:codigoDetalle de cuenta.
PUT/api/command/manual-cuentas/:codigoUpsert.
DELETE/api/command/manual-cuentas/:codigoEliminar.

routes/internal/manual-cuentas.ts expone un endpoint GET /api/internal/manual-cuentas protegido por token interno que retorna el dump completo (ManualCuentasRepository.listAllForExport) ordenado por código. Es consumido durante el pnpm build de este sitio para materializar las páginas estáticas del Manual de Cuentas.

Cuando opts.search está presente, la query usa $text con score:

Search query
const filter = { $text: { $search: search } };
const projection = { _id: 0, score: { $meta: 'textScore' } };
const sort = { score: { $meta: 'textScore' }, codigo: 1 };

El índice TEXT cubre debe, haber, nic_ifrs.tratamiento y tags. Los resultados se ordenan por relevancia y, en empate, por código ascendente. El campo score se elimina antes de retornar al cliente.

ErrorCausa
BadRequestError("codigo cuenta requerido")DTO sin codigo o vacío.
BadRequestError("codigo cuenta inexistente")El código no está en accounting_template.plan_contable.
Error("manual_cuenta_upsert_failed")El findByCodigo post-upsert no devolvió documento (caso casi imposible — indica corrupción).

Borrar un código del manual no elimina la cuenta del plan: son catálogos separados. La doctrina es: el plan es la verdad operacional (rige los movimientos contables); el manual es documentación. Una cuenta puede existir sin manual; un manual no puede existir sin plan.