Skip to content

Plataforma técnica · Orchestrator

Config Contable Service

Orchestrator Administración Configuracion

ConfigContableService es el wiring contable del tenant: la tabla de mapeo que responde dos preguntas operacionales:

  1. “Cuando llega un documento tributario tipo VENTA, ¿en qué cuenta del plan lo registro?”
  2. “Cuando se contabiliza un concepto de operación X, ¿qué cuenta va al debe y cuál al haber?”

Vive bajo domain/configContable/ pero sus tablas están en el schema operaciones_sii — refleja la realidad de que es config del flujo operacional, aunque conceptualmente sea administración.

Mapeo 1-a-1: tipo_documento → cuenta_contable_codigo.

CampoTipoNotas
tipo_documentoenum'VENTA' | 'BOLETA' | 'COMPRA' (PK).
cuenta_contable_codigostringFK lógica a administracion.plan_contable.

Mapeo 1-a-2: concepto_codigo → (cuenta_debito, cuenta_credito).

CampoTipoNotas
concepto_codigostringFK lógica a operaciones_sii.conceptos_operaciones. PK.
cuenta_debito_codigostringVa al debe del asiento.
cuenta_credito_codigostringVa al haber.

conceptos_operaciones es el catálogo de “qué cosa contable estamos haciendo” (e.g. RETENCION_HONORARIO, IVA_DEBITO_FISCAL, INGRESO_FACTURA_AFECTA). Cada uno necesita un par cuenta-debe / cuenta-haber para generar el asiento.

flowchart LR
  R["routes/admin/config-contable.ts"]
  S["ConfigContableService"]
  REP["ConfigContableRepository"]
  T1[("operaciones_sii.config_cuentas_contables")]
  T2[("operaciones_sii.config_conceptos_contables")]
  PC[("administracion.plan_contable")]
  CO[("operaciones_sii.conceptos_operaciones")]

  R --> S --> REP --> T1 & T2
  S -. valida FK .-> PC & CO

Operaciones de Cuentas (por tipo de documento)

Section titled “Operaciones de Cuentas (por tipo de documento)”

Lista todas las configs (ORDER BY tipo_documento).

Lookup por tipo_documento. Lanza NotFoundError("ConfigCuentaContable", tipoDocumento).

flowchart TB
  IN["createCuenta(data)"]
  V1["validateRequired<br/>(tipo_documento, cuenta_contable_codigo)"]
  V2["assertTipoDocumento ∈ {VENTA, BOLETA, COMPRA}"]
  V3["findCuenta(tipo_documento)"]
  EXIST{"ya existe?"}
  V4["assertCuentaEnPlan<br/>(plan_contable)"]
  INS["INSERT"]

  IN --> V1 --> V2 --> V3 --> EXIST
  EXIST -- sí --> ERR["ValidationError<br/>'Use PUT para actualizar'"]
  EXIST -- no --> V4 --> INS

Por qué rechaza si ya existe en vez de upsert: separación explícita de intención (POST = crear, PUT = actualizar). Para evitar el 409 Conflict accidental cuando alguien hace POST creyendo que es idempotente.

updateCuenta(ctx, tipoDocumento, cuentaContableCodigo)

Section titled “updateCuenta(ctx, tipoDocumento, cuentaContableCodigo)”

Solo cambia cuenta_contable_codigo. Valida que sea un código existente en el plan. Lanza NotFoundError si el tipo_documento no tiene config previa.

Valida tipo_documento y borra. Lanza NotFoundError si no existía.

listConceptos(ctx) / getConcepto(ctx, conceptoCodigo)

Section titled “listConceptos(ctx) / getConcepto(ctx, conceptoCodigo)”

Análogos a listCuentas / getCuenta pero sobre la otra tabla.

Triple validación FK (única operación en este service con tres):

flowchart TB
  IN["createConcepto(data)"]
  V1["validateRequired<br/>(concepto_codigo,<br/>cuenta_debito, cuenta_credito)"]
  V2["findConcepto(concepto_codigo)"]
  EXIST{"ya existe?"}
  V3["conceptoOperacionExiste<br/>(operaciones_sii.conceptos_operaciones)"]
  V4["assertCuentaEnPlan<br/>(cuenta_debito)"]
  V5["assertCuentaEnPlan<br/>(cuenta_credito)"]
  INS["INSERT"]

  IN --> V1 --> V2 --> EXIST
  EXIST -- sí --> ERR1["ValidationError<br/>'Use PUT'"]
  EXIST -- no --> V3
  V3 -- no --> ERR2["ValidationError<br/>'concepto no existe en<br/>operaciones_sii.conceptos_operaciones'"]
  V3 -- sí --> V4 --> V5 --> INS

updateConcepto(ctx, conceptoCodigo, updates)

Section titled “updateConcepto(ctx, conceptoCodigo, updates)”

Requiere al menos uno de cuenta_debito_codigo o cuenta_credito_codigo — si vienen ambos undefined lanza ValidationError("Debe enviar al menos cuenta_debito_codigo o cuenta_credito_codigo").

Valida los que sí vengan contra plan_contable. Construye SET dinámico solo con los campos definidos.

Borrado directo. Lanza NotFoundError si no existía.

HelperSchema fuenteSi falla
assertTipoDocumento(tipo)enum local TIPOS_DOCUMENTO_VALIDOSValidationError("tipo_documento inválido: ...")
assertCuentaEnPlan(ctx, codigo)administracion.plan_contableValidationError("cuenta X no existe en plan")
repo.conceptoOperacionExiste(pool, codigo)operaciones_sii.conceptos_operacionesValidationError("concepto no existe en ...")

Los tres son lookups SELECT EXISTS(...) dedicados — más baratos que un SELECT * y dan mensaje claro al usuario en vez de un 23503 foreign_key_violation crudo de Postgres.

  • Sin TX: cada operación es un solo statement (INSERT/UPDATE/DELETE) más validaciones de lectura. No hay invariante multi-tabla.
  • Sin hooks: cambiar el wiring contable es config — los asientos que se generen después del cambio usarán la nueva config; los anteriores ya quedaron escritos con la vieja. No hay nada que reactivar.

Si en el futuro se necesita “recontabilizar histórico con la nueva config”, esa sería una operación batch separada (probablemente en domain/ciclo-contable/), no un hook sobre cada update.

Quién consulta estas dos tablas durante operación normal:

ServicioTabla consultadaPara qué
OperacionesSiiService (ventas/compras)config_cuentas_contablesResolver cuenta de cabecera del asiento según tipo_documento.
AccountingService (asientos genéricos)config_conceptos_contablesResolver par debe/haber por concepto_codigo.
HonorariosServiceconfig_conceptos_contablesMapear RETENCION_HONORARIO, etc.
DeclaracionesService (F29)AmbasValidar que toda la operación del período tenga wiring resuelto.

Si una operación contable se ejecuta y la config falta, el servicio consumidor lanza ValidationError("falta configurar cuenta para ...") apuntando al UI de admin para que el operador la cree.

Documentados en Administración API. Resumen:

MétodoRutaService call
GET/api/admin/config-contable/cuentaslistCuentas(ctx)
GET/api/admin/config-contable/cuentas/:tipo_documentogetCuenta(ctx, tipo)
POST/api/admin/config-contable/cuentascreateCuenta(ctx, data)
PUT/api/admin/config-contable/cuentas/:tipo_documentoupdateCuenta(ctx, tipo, cuenta)
DELETE/api/admin/config-contable/cuentas/:tipo_documentodeleteCuenta(ctx, tipo)
GET/api/admin/config-contable/conceptoslistConceptos(ctx)
GET/api/admin/config-contable/conceptos/:concepto_codigogetConcepto(ctx, codigo)
POST/api/admin/config-contable/conceptoscreateConcepto(ctx, data)
PUT/api/admin/config-contable/conceptos/:concepto_codigoupdateConcepto(ctx, codigo, updates)
DELETE/api/admin/config-contable/conceptos/:concepto_codigodeleteConcepto(ctx, codigo)